/*  optimal cube solver  by  michael reid  reid@math.ucf.edu  */
/*  version 2.1  june 3, 2004  */
/*  symmetry mechanism added  */
/*  malloc.h removed, long string fixed  */


#define  USE_METRIC        QUARTER_TURN_METRIC
#define  SEARCH_LIMIT                        0
#define  USE_SYMMETRY                        1
#define  ONE_SOLUTION_ONLY                   1


#include  <stdio.h>
#include  <stdlib.h>
#include  <string.h>
#include  <setjmp.h>
#include  <signal.h>
//#include <stream.h>



#define  QUARTER_TURN_METRIC                 0
#define  FACE_TURN_METRIC                    1

#define  N_SYM                              16
#define  N_TWIST                            18
#define  N_CORNER                         2187
#define  N_ELOC                            495
#define  N_ELOC_CONV                      4096
#define  N_EFLIP                          2048
#define  N_FULLEDGE         (N_ELOC * N_EFLIP)
#define  N_EDGEQUOT                      64430
#define  N_CORNERPERM                    40320
#define  N_SLICEEDGE                     11880

#define  N_CUBESYM                          48
#define  N_SYMSUBGRP                        98
#define  SUBGRP_TRIVIAL                     97

#define  N_DIST_CHARS                  ((N_EDGEQUOT * N_CORNER) / 2)
#define  BACKWARDS_SWITCH_POINT    ((2 * N_EDGEQUOT * N_CORNER) / 5)

#define  CORNER_START                        0
#define  EDGE_START                      64244
#define  CORNERPERM_START                    0
#define  UD_SLICEEDGE_START                494
#define  RL_SLICEEDGE_START                 54
#define  FB_SLICEEDGE_START                209

#define  MAX_PERM_N                         12
#define  MAX_CHECK_PERM_N                   24

#define  MAX_TWISTS                         43

#define  TWIST_F                             0
#define  TWIST_F2                            1
#define  TWIST_F3                            2
#define  TWIST_R                             3
#define  TWIST_R2                            4
#define  TWIST_R3                            5
#define  TWIST_U                             6
#define  TWIST_U2                            7
#define  TWIST_U3                            8
#define  TWIST_B                             9
#define  TWIST_B2                           10
#define  TWIST_B3                           11
#define  TWIST_L                            12
#define  TWIST_L2                           13
#define  TWIST_L3                           14
#define  TWIST_D                            15
#define  TWIST_D2                           16
#define  TWIST_D3                           17

#define  CORNER_UFR                          0
#define  CORNER_URB                          1
#define  CORNER_UBL                          2
#define  CORNER_ULF                          3
#define  CORNER_DRF                          4
#define  CORNER_DFL                          5
#define  CORNER_DLB                          6
#define  CORNER_DBR                          7

#define  CORNER_FRU                          8
#define  CORNER_RBU                          9
#define  CORNER_BLU                         10
#define  CORNER_LFU                         11
#define  CORNER_RFD                         12
#define  CORNER_FLD                         13
#define  CORNER_LBD                         14
#define  CORNER_BRD                         15

#define  CORNER_RUF                         16
#define  CORNER_BUR                         17
#define  CORNER_LUB                         18
#define  CORNER_FUL                         19
#define  CORNER_FDR                         20
#define  CORNER_LDF                         21
#define  CORNER_BDL                         22
#define  CORNER_RDB                         23


#define  EDGE_UF                             0
#define  EDGE_UR                             1
#define  EDGE_UB                             2
#define  EDGE_UL                             3
#define  EDGE_DF                             4
#define  EDGE_DR                             5
#define  EDGE_DB                             6
#define  EDGE_DL                             7
#define  EDGE_FR                             8
#define  EDGE_FL                             9
#define  EDGE_BR                            10
#define  EDGE_BL                            11

#define  EDGE_FU                            12
#define  EDGE_RU                            13
#define  EDGE_BU                            14
#define  EDGE_LU                            15
#define  EDGE_FD                            16
#define  EDGE_RD                            17
#define  EDGE_BD                            18
#define  EDGE_LD                            19
#define  EDGE_RF                            20
#define  EDGE_LF                            21
#define  EDGE_RB                            22
#define  EDGE_LB                            23


#define  FACE_F                              0
#define  FACE_R                              1
#define  FACE_U                              2
#define  FACE_B                              3
#define  FACE_L                              4
#define  FACE_D                              5


#define  N_FOLLOW                          873

#define  FOLLOW_INVALID                      0

#define  N_SYLLABLE                         32

#define  SYLLABLE_INVALID                    0
#define  SYLLABLE_NONE                       1
#define  SYLLABLE_F                          2
#define  SYLLABLE_F2                         3
#define  SYLLABLE_F3                         4
#define  SYLLABLE_R                          5
#define  SYLLABLE_R2                         6
#define  SYLLABLE_R3                         7
#define  SYLLABLE_U                          8
#define  SYLLABLE_U2                         9
#define  SYLLABLE_U3                        10
#define  SYLLABLE_B                         11
#define  SYLLABLE_B2                        12
#define  SYLLABLE_B3                        13
#define  SYLLABLE_L                         14
#define  SYLLABLE_L2                        15
#define  SYLLABLE_L3                        16
#define  SYLLABLE_D                         17
#define  SYLLABLE_D2                        18
#define  SYLLABLE_D3                        19
#define  SYLLABLE_FB                        20
#define  SYLLABLE_FB2                       21
#define  SYLLABLE_FB3                       22
#define  SYLLABLE_F2B2                      23
#define  SYLLABLE_RL                        24
#define  SYLLABLE_RL2                       25
#define  SYLLABLE_RL3                       26
#define  SYLLABLE_R2L2                      27
#define  SYLLABLE_UD                        28
#define  SYLLABLE_UD2                       29
#define  SYLLABLE_UD3                       30
#define  SYLLABLE_U2D2                      31



#define  DIST(c, e)     (((e) & 0x1) ? (((int)distance[c][(e) >> 1]) >> 4)   \
	: (((int)distance[c][(e) >> 1]) & 0xF))



typedef struct cube
{
	int             edges[24];
	int             corners[24];
}
Cube;


typedef struct coset_coord
{
	int             corner_state;
	int             edge_state;
	int             sym_state;
}
Coset_coord;


typedef struct full_cube
{
	Cube            cubies;
	int             cornerperm;
	int             ud_sliceedge;
	int             rl_sliceedge;
	int             fb_sliceedge;
	Coset_coord     ud;
	Coset_coord     rl;
	Coset_coord     fb;
	int             parity;
	int             sym_subgrp;
}
Full_cube;


typedef struct search_data
{
	int             depth;
	int             found;
	int             found_quot;
	int            *multiplicities;
	int            *stabilizers;
}
Search_data;


typedef struct search_node
{
	int             remain_depth;
	int             twist;
	int             follow_type;
	Coset_coord     ud;
	Coset_coord     rl;
	Coset_coord     fb;
}
Search_node;


typedef struct metric_data
{
	int             metric;
	char            metric_char;
	int             twist_length[N_TWIST];
	int             increment;
}
Metric_data;


typedef struct options
{
	int             use_symmetry;
	int             search_limit;
	int             one_solution_only;
}
Options;


typedef struct subgroup_list
{
	int             n_subgroups;
	int           (*subgroups)[N_CUBESYM];
}
Subgroup_list;



static unsigned char   *sym_x_invsym_to_sym[N_SYM];

static unsigned char   *invsym_on_twist_ud[N_SYM];
static unsigned char   *invsym_on_twist_rl[N_SYM];
static unsigned char   *invsym_on_twist_fb[N_SYM];

static unsigned short  *twist_on_corner[N_TWIST];
static unsigned short  *sym_on_corner[N_SYM];

static unsigned short  *fulledge_to_edge;
static unsigned char   *fulledge_to_sym;

static unsigned short  *twist_on_edge[N_TWIST];
static unsigned char   *twist_x_edge_to_sym[N_TWIST];

static unsigned short  *twist_on_cornerperm[N_TWIST];
static unsigned short  *twist_on_sliceedge[N_TWIST];

static unsigned short  *twist_on_follow[N_TWIST];

static unsigned char   *distance[N_CORNER];

static char            *edge_cubie_str[] = {"UF", "UR", "UB", "UL",
"DF", "DR", "DB", "DL",
"FR", "FL", "BR", "BL",
"FU", "RU", "BU", "LU",
"FD", "RD", "BD", "LD",
"RF", "LF", "RB", "LB"};

static char            *corner_cubie_str[] = {"UFR", "URB", "UBL", "ULF",
"DRF", "DFL", "DLB", "DBR",
"FRU", "RBU", "BLU", "LFU",
"RFD", "FLD", "LBD", "BRD",
"RUF", "BUR", "LUB", "FUL",
"FDR", "LDF", "BDL", "RDB"};

static Metric_data     p_current_metric;
//static Options         p_current_options;
//
static unsigned int     n_nodes;
static unsigned int     n_tests;
static int              sol_found;

static int       jump_env;


/* ========================================================================= */
void  exit_w_error_message(char  *msg)
/* ------------------------------------------------------------------------- */

{
	printf("\n%s\n", msg);
	exit(EXIT_FAILURE);

	return;
}


/* ========================================================================= */
void  user_interrupt(int  unused_arg)
/* ------------------------------------------------------------------------- */

{
	//printf("\n-- user interrupt --\n");
	fflush(stdout);
	//siglongjmp(jump_env, 1);

	return;
}


/* ========================================================================= */
void  perm_n_unpack(int  nn, int  indx, int  array_out[])
/* ------------------------------------------------------------------------- */

{
	int                     ii, jj;


	for (ii = nn - 1; ii >= 0; ii--)
	{
		array_out[ii] = indx % (nn - ii);
		indx /= (nn - ii);

		for (jj = ii + 1; jj < nn; jj++)
			if (array_out[jj] >= array_out[ii])
				array_out[jj]++;
	}

	return;
}


/* ========================================================================= */
int  perm_n_pack(int  nn, int  array_in[])
/* ------------------------------------------------------------------------- */

{
	int                     indx, ii, jj;


	indx = 0;

	for (ii = 0; ii < nn; ii++)
	{
		indx *= (nn - ii);

		for (jj = ii + 1; jj < nn; jj++)
			if (array_in[jj] < array_in[ii])
				indx++;
	}

	return indx;
}


/* ========================================================================= */
int  perm_n_check(int  nn, int  array_in[])
/* ------------------------------------------------------------------------- */

{
	int                     count[MAX_CHECK_PERM_N], ii;


	for (ii = 0; ii < nn; ii++)
		count[ii] = 0;

	for (ii = 0; ii < nn; ii++)
	{
		if ((array_in[ii] < 0) || (array_in[ii] >= nn))
			return 1;

		count[array_in[ii]]++;
	}

	for (ii = 0; ii < nn; ii++)
		if (count[ii] != 1)
			return 1;

	return 0;
}


/* ========================================================================= */
int  perm_n_parity(int  nn, int  array_in[])
/* ------------------------------------------------------------------------- */

{
	int                     temp_array[MAX_CHECK_PERM_N];
	int                     ii, jj, n_cycles;


	for (ii = 0; ii < nn; ii++)
		temp_array[ii] = 0;

	n_cycles = 0;

	for (ii = 0; ii < nn; ii++)
		if (temp_array[ii] == 0)
		{
			n_cycles++;
			jj = ii;
			while (temp_array[jj] == 0)
			{
				temp_array[jj] = 1;
				jj = array_in[jj];
			}
		}

		return (n_cycles + nn) % 2;
}


/* ========================================================================= */
void  perm_n_init(int  nn, int  array_out[])
/* ------------------------------------------------------------------------- */

{
	int                     ii;


	for (ii = 0; ii < nn; ii++)
		array_out[ii] = ii;

	return;
}


/* ========================================================================= */
void  perm_n_compose(int  nn, int  perm0_in[], int  perm1_in[],
							int  perm_out[])
							/* ------------------------------------------------------------------------- */

{
	int                     ii;


	for (ii = 0; ii < nn; ii++)
		perm_out[ii] = perm0_in[perm1_in[ii]];

	return;
}


/* ========================================================================= */
void  perm_n_conjugate(int  nn, int  arr_in[], int  conjugator[],
							  int  array_out[])
							  /* ------------------------------------------------------------------------- */

{
	int                     ii;


	for (ii = 0; ii < nn; ii++)
		array_out[conjugator[ii]] = conjugator[arr_in[ii]];

	return;
}


/* ========================================================================= */
void  two_cycle(int  array[], int  ind0, int  ind1)
/* ------------------------------------------------------------------------- */

{
	int                     temp;


	temp = array[ind0];
	array[ind0] = array[ind1];
	array[ind1] = temp;

	return;
}


/* ========================================================================= */
void  three_cycle(int  array[], int  ind0, int  ind1, int  ind2)
/* ------------------------------------------------------------------------- */

{
	int                     temp;


	temp = array[ind0];
	array[ind0] = array[ind1];
	array[ind1] = array[ind2];
	array[ind2] = temp;

	return;
}


/* ========================================================================= */
void  four_cycle(int  array[], int  ind0, int  ind1, int  ind2, int  ind3)
/* ------------------------------------------------------------------------- */

{
	int                     temp;


	temp = array[ind0];
	array[ind0] = array[ind1];
	array[ind1] = array[ind2];
	array[ind2] = array[ind3];
	array[ind3] = temp;

	return;
}


/* ========================================================================= */
void  print_cube(Cube  *p_cube)
/* ------------------------------------------------------------------------- */

{
	//printf("%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n",
	//    edge_cubie_str[p_cube->edges[0]], edge_cubie_str[p_cube->edges[1]],
	//    edge_cubie_str[p_cube->edges[2]], edge_cubie_str[p_cube->edges[3]],
	//    edge_cubie_str[p_cube->edges[4]], edge_cubie_str[p_cube->edges[5]],
	//    edge_cubie_str[p_cube->edges[6]], edge_cubie_str[p_cube->edges[7]],
	//    edge_cubie_str[p_cube->edges[8]], edge_cubie_str[p_cube->edges[9]],
	//    edge_cubie_str[p_cube->edges[10]], edge_cubie_str[p_cube->edges[11]],
	//corner_cubie_str[p_cube->corners[0]], corner_cubie_str[p_cube->corners[1]],
	//corner_cubie_str[p_cube->corners[2]], corner_cubie_str[p_cube->corners[3]],
	//corner_cubie_str[p_cube->corners[4]], corner_cubie_str[p_cube->corners[5]],
	//corner_cubie_str[p_cube->corners[6]], corner_cubie_str[p_cube->corners[7]]);

	return;
}


/* ========================================================================= */
void  cube_init(Cube  *p_cube)
/* ------------------------------------------------------------------------- */

{
	perm_n_init(24, p_cube->edges);
	perm_n_init(24, p_cube->corners);

	return;
}


/* ========================================================================= */
int  cube_compare(Cube  *cube0, Cube  *cube1)
/* ------------------------------------------------------------------------- */

{
	int           ii;

	for (ii = 0; ii < 24; ii++)
	{
		if (cube0->edges[ii] < cube1->edges[ii])
			return -1;
		else if (cube0->edges[ii] > cube1->edges[ii])
			return 1;
	}

	for (ii = 0; ii < 24; ii++)
	{
		if (cube0->corners[ii] < cube1->corners[ii])
			return -1;
		else if (cube0->corners[ii] > cube1->corners[ii])
			return 1;
	}

	return 0;
}


/* ========================================================================= */
void  cube_compose(Cube  *in_cube0, Cube  *in_cube1, Cube  *out_cube)
/* ------------------------------------------------------------------------- */

{
	perm_n_compose(24, in_cube0->edges, in_cube1->edges, out_cube->edges);
	perm_n_compose(24, in_cube0->corners, in_cube1->corners, out_cube->corners);

	return;
}


/* ========================================================================= */
void  cube_conjugate(Cube  *p_cube_in, Cube  *p_conjugator,
							Cube  *p_cube_out)
							/* ------------------------------------------------------------------------- */

{
	perm_n_conjugate(24, p_cube_in->edges, p_conjugator->edges, p_cube_out->edges);
	perm_n_conjugate(24, p_cube_in->corners, p_conjugator->corners,
		p_cube_out->corners);

	return;
}


/* ========================================================================= */
int  cube_is_solved(Cube  *p_cube)
/* ------------------------------------------------------------------------- */

{
	Cube                    temp_cube;


	cube_init(&temp_cube);

	return (cube_compare(p_cube, &temp_cube) == 0);
}


/* ========================================================================= */
int  metric_q_length(int  twist)
/* ------------------------------------------------------------------------- */

{
	if ((twist == TWIST_F) || (twist == TWIST_F3) ||
		(twist == TWIST_R) || (twist == TWIST_R3) ||
		(twist == TWIST_U) || (twist == TWIST_U3) ||
		(twist == TWIST_B) || (twist == TWIST_B3) ||
		(twist == TWIST_L) || (twist == TWIST_L3) ||
		(twist == TWIST_D) || (twist == TWIST_D3))
		return 1;
	else if ((twist == TWIST_F2) || (twist == TWIST_R2) || (twist == TWIST_U2) ||
		(twist == TWIST_B2) || (twist == TWIST_L2) || (twist == TWIST_D2))
		return 2;
	else
		exit_w_error_message("metric_q_length : invalid twist");

	return 0;
}


/* ========================================================================= */
int  metric_f_length(int  twist)
/* ------------------------------------------------------------------------- */

{
	if ((twist == TWIST_F) || (twist == TWIST_F2) || (twist == TWIST_F3) ||
		(twist == TWIST_R) || (twist == TWIST_R2) || (twist == TWIST_R3) ||
		(twist == TWIST_U) || (twist == TWIST_U2) || (twist == TWIST_U3) ||
		(twist == TWIST_B) || (twist == TWIST_B2) || (twist == TWIST_B3) ||
		(twist == TWIST_L) || (twist == TWIST_L2) || (twist == TWIST_L3) ||
		(twist == TWIST_D) || (twist == TWIST_D2) || (twist == TWIST_D3))
		return 1;
	else
		exit_w_error_message("metric_f_length : invalid twist");

	return 0;
}


/* ========================================================================= */
void  calc_metric_q_length(int  lengths[N_TWIST])
/* ------------------------------------------------------------------------- */

{
	int                     twist;


	for (twist = 0; twist < N_TWIST; twist++)
		lengths[twist] = metric_q_length(twist);

	return;
}


/* ========================================================================= */
void  calc_metric_f_length(int  lengths[N_TWIST])
/* ------------------------------------------------------------------------- */

{
	int                     twist;


	for (twist = 0; twist < N_TWIST; twist++)
		lengths[twist] = metric_f_length(twist);

	return;
}


/* ========================================================================= */
void  init_metric(Metric_data  *p_metric_data, int  use_metric)
/* ------------------------------------------------------------------------- */

{
	if ((use_metric != QUARTER_TURN_METRIC) && (use_metric != FACE_TURN_METRIC))
		exit_w_error_message("init_metric : unknown metric");

	p_metric_data->metric = use_metric;

	if (p_metric_data->metric == QUARTER_TURN_METRIC)
	{
		//printf("using quarter turn metric\n");
		p_metric_data->metric_char = 'q';
		calc_metric_q_length(p_metric_data->twist_length);
		p_metric_data->increment = 2;
	}
	else
	{
		//printf("using face turn metric\n");
		p_metric_data->metric_char = 'f';
		calc_metric_f_length(p_metric_data->twist_length);
		p_metric_data->increment = 1;
	}
	memcpy(&p_current_metric,p_metric_data,sizeof(p_current_metric));
	//p_current_metric = p_metric_data;

	return;
}


/* ========================================================================= */
void __declspec(dllexport) init_options(Metric_data  *p_metric_data, Options  *p_user_options)
/* ------------------------------------------------------------------------- */

{
	init_metric(p_metric_data, USE_METRIC);

	p_user_options->search_limit = SEARCH_LIMIT;
	//if (p_user_options->search_limit <= 0)
	//   printf("no search limit\n");
	//else
	//   printf("search limit: %d%c\n", p_user_options->search_limit,
	//                                                p_metric_data->metric_char);

	p_user_options->use_symmetry = USE_SYMMETRY;
	//if (p_user_options->use_symmetry)
	//   printf("using symmetry reductions\n");
	//else
	//   printf("not using symmetry reductions\n");

	p_user_options->one_solution_only = ONE_SOLUTION_ONLY;
	//if (p_user_options->one_solution_only)
	//   printf("only finding one solution\n");
	//else
	//   printf("finding all solutions\n");

	//printf("\n");
	//memcpy(&p_current_options,p_user_options,sizeof(p_current_options));


	return;
}


/* ========================================================================= */
void  calc_group_table(int  group_table[N_CUBESYM][N_CUBESYM])
/* ------------------------------------------------------------------------- */

{
	int                     sym_array[N_CUBESYM][6];
	int                     sym_pack[N_CUBESYM];
	int                     temp_array[6];
	int                     ii, jj, kk, pack;


	perm_n_init(6, sym_array[0]);
	perm_n_init(6, sym_array[1]);

	four_cycle(sym_array[1], FACE_F, FACE_L, FACE_B, FACE_R);

	for (ii = 2; ii < 4; ii++)
		perm_n_compose(6, sym_array[1], sym_array[ii - 1], sym_array[ii]);

	perm_n_init(6, sym_array[4]);

	two_cycle(sym_array[4], FACE_U, FACE_D);
	two_cycle(sym_array[4], FACE_R, FACE_L);

	for (ii = 5; ii < 8; ii++)
		perm_n_compose(6, sym_array[4], sym_array[ii - 4], sym_array[ii]);

	perm_n_init(6, sym_array[8]);

	two_cycle(sym_array[8], FACE_U, FACE_D);

	for (ii = 9; ii < 16; ii++)
		perm_n_compose(6, sym_array[8], sym_array[ii - 8], sym_array[ii]);

	perm_n_init(6, sym_array[16]);

	three_cycle(sym_array[16], FACE_U, FACE_F, FACE_R);
	three_cycle(sym_array[16], FACE_D, FACE_B, FACE_L);

	for (ii = 17; ii < 48; ii++)
		perm_n_compose(6, sym_array[16], sym_array[ii - 16], sym_array[ii]);

	for (ii = 0; ii < N_CUBESYM; ii++)
		sym_pack[ii] = perm_n_pack(6, sym_array[ii]);

	for (ii = 0; ii < N_CUBESYM; ii++)
		for (jj = 0; jj < N_CUBESYM; jj++)
		{
			perm_n_compose(6, sym_array[ii], sym_array[jj], temp_array);
			pack = perm_n_pack(6, temp_array);

			for (kk = 0; kk < N_CUBESYM; kk++)
				if (sym_pack[kk] == pack)
				{
					group_table[ii][jj] = kk;
					break;
				}

				if (kk == N_CUBESYM)
					exit_w_error_message("calc_group_table : product not found");
		}

		return;
}


/* ========================================================================= */
void  init_sym_x_invsym_to_sym(void)
/* ------------------------------------------------------------------------- */

{
	unsigned char         (*mem_ptr)[N_SYM];
	int                     group_table[N_CUBESYM][N_CUBESYM];
	int                     group_inverse[N_CUBESYM];
	int                     ii, jj;


	/*  initialize global array   sym_x_invsym_to_sym   */

	mem_ptr =
		(unsigned char (*)[N_SYM])malloc(sizeof(unsigned char [N_SYM][N_SYM]));
	if (mem_ptr == NULL)
		exit_w_error_message("init_sym_x_invsym_to_sym : couldn't get memory");

	for (ii = 0; ii < N_SYM; ii++)
		sym_x_invsym_to_sym[ii] = mem_ptr[ii];

	calc_group_table(group_table);

	for (ii = 0; ii < N_SYM; ii++)
	{
		for (jj = 0; jj < N_SYM; jj++)
			if (group_table[ii][jj] == 0)
			{
				group_inverse[ii] = jj;
				break;
			}

			if (jj == N_SYM)
				exit_w_error_message("init_sym_x_invsym_to_sym : inverse not found");
	}

	for (ii = 0; ii < N_SYM; ii++)
		for (jj = 0; jj < N_SYM; jj++)
			sym_x_invsym_to_sym[ii][jj] =
			(unsigned char)group_table[ii][group_inverse[jj]];

	return;
}


/* ========================================================================= */
void  calc_sym_on_twist(int  sym_on_twist[N_CUBESYM][N_TWIST])
/* ------------------------------------------------------------------------- */

{
	int                     sym;


	perm_n_init(N_TWIST, sym_on_twist[0]);
	perm_n_init(N_TWIST, sym_on_twist[1]);

	four_cycle(sym_on_twist[1], TWIST_F , TWIST_L , TWIST_B , TWIST_R );
	four_cycle(sym_on_twist[1], TWIST_F2, TWIST_L2, TWIST_B2, TWIST_R2);
	four_cycle(sym_on_twist[1], TWIST_F3, TWIST_L3, TWIST_B3, TWIST_R3);

	for (sym = 2; sym < 4; sym++)
		perm_n_compose(N_TWIST, sym_on_twist[1], sym_on_twist[sym - 1],
		sym_on_twist[sym]);

	perm_n_init(N_TWIST, sym_on_twist[4]);

	two_cycle(sym_on_twist[4], TWIST_R , TWIST_L );
	two_cycle(sym_on_twist[4], TWIST_R2, TWIST_L2);
	two_cycle(sym_on_twist[4], TWIST_R3, TWIST_L3);
	two_cycle(sym_on_twist[4], TWIST_U , TWIST_D );
	two_cycle(sym_on_twist[4], TWIST_U2, TWIST_D2);
	two_cycle(sym_on_twist[4], TWIST_U3, TWIST_D3);

	for (sym = 5; sym < 8; sym++)
		perm_n_compose(N_TWIST, sym_on_twist[4], sym_on_twist[sym - 4],
		sym_on_twist[sym]);

	perm_n_init(N_TWIST, sym_on_twist[8]);

	two_cycle(sym_on_twist[8], TWIST_F, TWIST_F3);
	two_cycle(sym_on_twist[8], TWIST_R, TWIST_R3);
	two_cycle(sym_on_twist[8], TWIST_B, TWIST_B3);
	two_cycle(sym_on_twist[8], TWIST_L, TWIST_L3);
	two_cycle(sym_on_twist[8], TWIST_U , TWIST_D3);
	two_cycle(sym_on_twist[8], TWIST_U2, TWIST_D2);
	two_cycle(sym_on_twist[8], TWIST_U3, TWIST_D );

	for (sym = 9; sym < 16; sym++)
		perm_n_compose(N_TWIST, sym_on_twist[8], sym_on_twist[sym - 8],
		sym_on_twist[sym]);

	perm_n_init(N_TWIST, sym_on_twist[16]);

	three_cycle(sym_on_twist[16], TWIST_F , TWIST_R , TWIST_U );
	three_cycle(sym_on_twist[16], TWIST_F2, TWIST_R2, TWIST_U2);
	three_cycle(sym_on_twist[16], TWIST_F3, TWIST_R3, TWIST_U3);
	three_cycle(sym_on_twist[16], TWIST_B , TWIST_L , TWIST_D );
	three_cycle(sym_on_twist[16], TWIST_B2, TWIST_L2, TWIST_D2);
	three_cycle(sym_on_twist[16], TWIST_B3, TWIST_L3, TWIST_D3);

	for (sym = 17; sym < 48; sym++)
		perm_n_compose(N_TWIST, sym_on_twist[16], sym_on_twist[sym - 16],
		sym_on_twist[sym]);

	return;
}


/* ========================================================================= */
void  init_invsym_on_twist(void)
/* ------------------------------------------------------------------------- */

{
	unsigned char         (*mem_ptr)[N_SYM][N_TWIST];
	int                     sym_on_twist[N_CUBESYM][N_TWIST];
	int                     ud_to_rl[N_TWIST], sym, twist;


	/*   allocate and initialize global arrays                                 */
	/*   invsym_on_twist_ud ,  invsym_on_twist_fb   and   invsym_on_twist_rl   */

	mem_ptr = (unsigned char (*)[N_SYM][N_TWIST])
		malloc(sizeof(unsigned char [3][N_SYM][N_TWIST]));
	if (mem_ptr == NULL)
		exit_w_error_message("init_invsym_on_twist : couldn't get memory");

	for (sym = 0; sym < N_SYM; sym++)
	{
		invsym_on_twist_ud[sym] = mem_ptr[0][sym];
		invsym_on_twist_rl[sym] = mem_ptr[1][sym];
		invsym_on_twist_fb[sym] = mem_ptr[2][sym];
	}

	calc_sym_on_twist(sym_on_twist);

	for (sym = 0; sym < N_SYM; sym++)
		for (twist = 0; twist < N_TWIST; twist++)
			invsym_on_twist_ud[sym][twist] =
			(unsigned char)sym_on_twist[(int)sym_x_invsym_to_sym[0][sym]][twist];

	perm_n_init(N_TWIST, ud_to_rl);

	three_cycle(ud_to_rl, TWIST_F , TWIST_R , TWIST_U );
	three_cycle(ud_to_rl, TWIST_F2, TWIST_R2, TWIST_U2);
	three_cycle(ud_to_rl, TWIST_F3, TWIST_R3, TWIST_U3);
	three_cycle(ud_to_rl, TWIST_B , TWIST_L , TWIST_D );
	three_cycle(ud_to_rl, TWIST_B2, TWIST_L2, TWIST_D2);
	three_cycle(ud_to_rl, TWIST_B3, TWIST_L3, TWIST_D3);

	for (sym = 0; sym < N_SYM; sym++)
		for (twist = 0; twist < N_TWIST; twist++)
			invsym_on_twist_rl[sym][twist] =
			invsym_on_twist_ud[sym][ud_to_rl[twist]];

	for (sym = 0; sym < N_SYM; sym++)
		for (twist = 0; twist < N_TWIST; twist++)
			invsym_on_twist_fb[sym][twist] =
			invsym_on_twist_rl[sym][ud_to_rl[twist]];

	return;
}


/* ========================================================================= */
void  generate_subgroup(int  group_table[N_CUBESYM][N_CUBESYM],
								int   gen_set[N_CUBESYM])
								/* ------------------------------------------------------------------------- */

								/*  gen_set[]  is both input and output  */

{
	int                 found, ii, jj;


	gen_set[0] = 1;
	found = 1;

	while (found)
	{
		found = 0;

		for (ii = 0; ii < N_CUBESYM; ii++)
			if (gen_set[ii] != 0)
				for (jj = 0; jj < N_CUBESYM; jj++)
					if ((gen_set[jj] != 0) && (gen_set[group_table[ii][jj]] == 0))
					{
						gen_set[group_table[ii][jj]] = 1;
						found = 1;
					}
	}

	return;
}


/* ========================================================================= */
void  calc_subgroups_recursive(int  group_table[N_CUBESYM][N_CUBESYM],
										 int  contain_list[N_CUBESYM],
										 int  avoid_list[N_CUBESYM],
										 Subgroup_list  *p_output)
										 /* ------------------------------------------------------------------------- */

{
	int                 local_contain_list[N_CUBESYM], ii, jj;


	for (ii = 0; ii < N_CUBESYM; ii++)
		if (contain_list[ii] && avoid_list[ii])
			return;

	for (ii = 0; ii < N_CUBESYM; ii++)
		if ((contain_list[ii] == 0) && (avoid_list[ii] == 0))
			break;

	if (ii < N_CUBESYM)
	{
		for (jj = 0; jj < N_CUBESYM; jj++)
			local_contain_list[jj] = contain_list[jj];
		local_contain_list[ii] = 1;
		generate_subgroup(group_table, local_contain_list);
		calc_subgroups_recursive(group_table, local_contain_list, avoid_list,
			p_output);
		avoid_list[ii] = 1;
		calc_subgroups_recursive(group_table, contain_list, avoid_list, p_output);
		avoid_list[ii] = 0;
	}
	else
	{
		if (p_output->n_subgroups >= N_SYMSUBGRP)
			exit_w_error_message("calc_subgroups_recursive : too many subgroups");

		for (ii = 0; ii < N_CUBESYM; ii++)
			p_output->subgroups[p_output->n_subgroups][ii] = contain_list[ii];

		p_output->n_subgroups++;
	}

	return;
}


/* ========================================================================= */
void  calc_subgroup_list(int  subgroup_list[N_SYMSUBGRP][N_CUBESYM])
/* ------------------------------------------------------------------------- */

{
	Subgroup_list   output_list;
	int             group_table[N_CUBESYM][N_CUBESYM];
	int             contain_list[N_CUBESYM];
	int             avoid_list[N_CUBESYM];
	int             ii;


	calc_group_table(group_table);

	output_list.n_subgroups = 0;
	output_list.subgroups = subgroup_list;

	contain_list[0] = 1;

	for (ii = 1; ii < N_CUBESYM; ii++)
		contain_list[ii] = 0;

	for (ii = 0; ii < N_CUBESYM; ii++)
		avoid_list[ii] = 0;

	calc_subgroups_recursive(group_table, contain_list, avoid_list, &output_list);

	if (output_list.n_subgroups != N_SYMSUBGRP)
		exit_w_error_message("calc_subgroup_list : wrong number of subgroups");

	return;
}


/* ========================================================================= */
int  is_single_twist(int  syllable)
/* ------------------------------------------------------------------------- */

{
	if ((syllable == SYLLABLE_F) || (syllable == SYLLABLE_F2) ||
		(syllable == SYLLABLE_F3) ||
		(syllable == SYLLABLE_R) || (syllable == SYLLABLE_R2) ||
		(syllable == SYLLABLE_R3) ||
		(syllable == SYLLABLE_U) || (syllable == SYLLABLE_U2) ||
		(syllable == SYLLABLE_U3) ||
		(syllable == SYLLABLE_B) || (syllable == SYLLABLE_B2) ||
		(syllable == SYLLABLE_B3) ||
		(syllable == SYLLABLE_L) || (syllable == SYLLABLE_L2) ||
		(syllable == SYLLABLE_L3) ||
		(syllable == SYLLABLE_D) || (syllable == SYLLABLE_D2) ||
		(syllable == SYLLABLE_D3))
		return 1;

	return 0;
}


/* ========================================================================= */
int  syllable_to_twist(int  syllable)
/* ------------------------------------------------------------------------- */

{
	if (syllable == SYLLABLE_F)
		return TWIST_F;
	else if (syllable == SYLLABLE_F2)
		return TWIST_F2;
	else if (syllable == SYLLABLE_F3)
		return TWIST_F3;
	else if (syllable == SYLLABLE_R)
		return TWIST_R;
	else if (syllable == SYLLABLE_R2)
		return TWIST_R2;
	else if (syllable == SYLLABLE_R3)
		return TWIST_R3;
	else if (syllable == SYLLABLE_U)
		return TWIST_U;
	else if (syllable == SYLLABLE_U2)
		return TWIST_U2;
	else if (syllable == SYLLABLE_U3)
		return TWIST_U3;
	else if (syllable == SYLLABLE_B)
		return TWIST_B;
	else if (syllable == SYLLABLE_B2)
		return TWIST_B2;
	else if (syllable == SYLLABLE_B3)
		return TWIST_B3;
	else if (syllable == SYLLABLE_L)
		return TWIST_L;
	else if (syllable == SYLLABLE_L2)
		return TWIST_L2;
	else if (syllable == SYLLABLE_L3)
		return TWIST_L3;
	else if (syllable == SYLLABLE_D)
		return TWIST_D;
	else if (syllable == SYLLABLE_D2)
		return TWIST_D2;
	else if (syllable == SYLLABLE_D3)
		return TWIST_D3;
	else
		exit_w_error_message("syllable_to_twist : invalid input");

	return -1;
}


/* ========================================================================= */
void  syllable_to_two_twists(int  syllable, int  twists_out[2])
/* ------------------------------------------------------------------------- */

{
	if (syllable == SYLLABLE_FB)
	{
		twists_out[0] = TWIST_F;
		twists_out[1] = TWIST_B;
	}
	else if (syllable == SYLLABLE_FB2)
	{
		twists_out[0] = TWIST_F;
		twists_out[1] = TWIST_B2;
	}
	else if (syllable == SYLLABLE_FB3)
	{
		twists_out[0] = TWIST_F;
		twists_out[1] = TWIST_B3;
	}
	else if (syllable == SYLLABLE_F2B2)
	{
		twists_out[0] = TWIST_F2;
		twists_out[1] = TWIST_B2;
	}
	else if (syllable == SYLLABLE_RL)
	{
		twists_out[0] = TWIST_R;
		twists_out[1] = TWIST_L;
	}
	else if (syllable == SYLLABLE_RL2)
	{
		twists_out[0] = TWIST_R;
		twists_out[1] = TWIST_L2;
	}
	else if (syllable == SYLLABLE_RL3)
	{
		twists_out[0] = TWIST_R;
		twists_out[1] = TWIST_L3;
	}
	else if (syllable == SYLLABLE_R2L2)
	{
		twists_out[0] = TWIST_R2;
		twists_out[1] = TWIST_L2;
	}
	else if (syllable == SYLLABLE_UD)
	{
		twists_out[0] = TWIST_U;
		twists_out[1] = TWIST_D;
	}
	else if (syllable == SYLLABLE_UD2)
	{
		twists_out[0] = TWIST_U;
		twists_out[1] = TWIST_D2;
	}
	else if (syllable == SYLLABLE_UD3)
	{
		twists_out[0] = TWIST_U;
		twists_out[1] = TWIST_D3;
	}
	else if (syllable == SYLLABLE_U2D2)
	{
		twists_out[0] = TWIST_U2;
		twists_out[1] = TWIST_D2;
	}
	else
		exit_w_error_message("syllable_to_two_twists : invalid input");

	return;
}


/* ========================================================================= */
int  twists_in_wrong_order(int  twists[2])
/* ------------------------------------------------------------------------- */

{
	if (((twists[0] == TWIST_B) || (twists[0] == TWIST_B2) ||
		(twists[0] == TWIST_B3)) &&
		((twists[1] == TWIST_F) || (twists[1] == TWIST_F2) ||
		(twists[1] == TWIST_F3)))
		return 1;

	if (((twists[0] == TWIST_L) || (twists[0] == TWIST_L2) ||
		(twists[0] == TWIST_L3)) &&
		((twists[1] == TWIST_R) || (twists[1] == TWIST_R2) ||
		(twists[1] == TWIST_R3)))
		return 1;

	if (((twists[0] == TWIST_D) || (twists[0] == TWIST_D2) ||
		(twists[0] == TWIST_D3)) &&
		((twists[1] == TWIST_U) || (twists[1] == TWIST_U2) ||
		(twists[1] == TWIST_U3)))
		return 1;

	return 0;
}


/* ========================================================================= */
void  clean_up_sequence(int  twist_sequence[], int  n_twists)
/* ------------------------------------------------------------------------- */

{
	int             ii;


	for (ii = 1; ii < n_twists; ii++)
		if (twists_in_wrong_order(&twist_sequence[ii - 1]) != 0)
			two_cycle(twist_sequence, ii - 1, ii);

	return;
}


/* ========================================================================= */
int  which_subgroup(int  subgroup[N_CUBESYM],
						  int  subgroup_list[N_SYMSUBGRP][N_CUBESYM])
						  /* ------------------------------------------------------------------------- */

{
	int             subgrp, sym;


	for (subgrp = 0; subgrp < N_SYMSUBGRP; subgrp++)
	{
		for (sym = 0; sym < N_CUBESYM; sym++)
			if ((subgroup[sym] == 0) != (subgroup_list[subgrp][sym] == 0))
				break;

		if (sym == N_CUBESYM)
			return subgrp;
	}

	return -1;
}


/* ========================================================================= */
void  calc_syllable_on_sym(int  subgroup_list[N_SYMSUBGRP][N_CUBESYM],
									int  sym_on_twist[N_CUBESYM][N_TWIST],
									int  syllable_on_sym[N_SYLLABLE][N_SYMSUBGRP])
									/* ------------------------------------------------------------------------- */

{
	int             subgroup[N_CUBESYM], temp_subgroup[N_CUBESYM];
	int             twist_arr[2], temp_arr[2];
	int             syllable, sym, subgrp, twist;


	for (syllable = 0; syllable < N_SYLLABLE; syllable++)
	{
		if ((syllable == SYLLABLE_INVALID) || (syllable == SYLLABLE_NONE))
		{
			subgroup[0] = 1;
			for (sym = 1; sym < N_CUBESYM; sym++)
				subgroup[sym] = 0;
		}
		else if (is_single_twist(syllable))
		{
			twist = syllable_to_twist(syllable);
			for (sym = 0; sym < N_CUBESYM; sym++)
				subgroup[sym] = (sym_on_twist[sym][twist] == twist);
		}
		else
		{
			syllable_to_two_twists(syllable, twist_arr);

			for (sym = 0; sym < N_CUBESYM; sym++)
			{
				temp_arr[0] = sym_on_twist[sym][twist_arr[0]];
				temp_arr[1] = sym_on_twist[sym][twist_arr[1]];
				clean_up_sequence(temp_arr, 2);
				if ((temp_arr[0] == twist_arr[0]) &&
					(temp_arr[1] == twist_arr[1]))
					subgroup[sym] = 1;
				else
					subgroup[sym] = 0;
			}
		}

		for (subgrp = 0; subgrp < N_SYMSUBGRP; subgrp++)
		{
			for (sym = 0; sym < N_CUBESYM; sym++)
				temp_subgroup[sym] = (subgroup[sym] && subgroup_list[subgrp][sym]);

			syllable_on_sym[syllable][subgrp] = which_subgroup(temp_subgroup,
				subgroup_list);
			if (syllable_on_sym[syllable][subgrp] < 0)
				exit_w_error_message("calc_syllable_on_sym : subgroup not found");
		}
	}

	return;
}


/* ========================================================================= */
int  twist_on_syllable(int  twist, int  syllable)
/* ------------------------------------------------------------------------- */

{
	if (syllable == SYLLABLE_INVALID)
		return SYLLABLE_INVALID;

	if (twist == TWIST_F)
	{
		if ((syllable == SYLLABLE_F) || (syllable == SYLLABLE_F2) ||
			(syllable == SYLLABLE_F3) ||
			(syllable == SYLLABLE_B) || (syllable == SYLLABLE_B2) ||
			(syllable == SYLLABLE_B3) ||
			(syllable == SYLLABLE_FB) || (syllable == SYLLABLE_FB2) ||
			(syllable == SYLLABLE_FB3) || (syllable == SYLLABLE_F2B2))
			return SYLLABLE_INVALID;
		else
			return SYLLABLE_F;
	}
	else if (twist == TWIST_F2)
	{
		if ((syllable == SYLLABLE_F) || (syllable == SYLLABLE_F2) ||
			(syllable == SYLLABLE_F3) ||
			(syllable == SYLLABLE_B) || (syllable == SYLLABLE_B2) ||
			(syllable == SYLLABLE_B3) ||
			(syllable == SYLLABLE_FB) || (syllable == SYLLABLE_FB2) ||
			(syllable == SYLLABLE_FB3) || (syllable == SYLLABLE_F2B2))
			return SYLLABLE_INVALID;
		else
			return SYLLABLE_F2;
	}
	else if (twist == TWIST_F3)
	{
		if ((syllable == SYLLABLE_F) || (syllable == SYLLABLE_F2) ||
			(syllable == SYLLABLE_F3) ||
			(syllable == SYLLABLE_B) || (syllable == SYLLABLE_B2) ||
			(syllable == SYLLABLE_B3) ||
			(syllable == SYLLABLE_FB) || (syllable == SYLLABLE_FB2) ||
			(syllable == SYLLABLE_FB3) || (syllable == SYLLABLE_F2B2))
			return SYLLABLE_INVALID;
		else
			return SYLLABLE_F3;
	}
	else if (twist == TWIST_R)
	{
		if ((syllable == SYLLABLE_R) || (syllable == SYLLABLE_R2) ||
			(syllable == SYLLABLE_R3) ||
			(syllable == SYLLABLE_L) || (syllable == SYLLABLE_L2) ||
			(syllable == SYLLABLE_L3) ||
			(syllable == SYLLABLE_RL) || (syllable == SYLLABLE_RL2) ||
			(syllable == SYLLABLE_RL3) || (syllable == SYLLABLE_R2L2))
			return SYLLABLE_INVALID;
		else
			return SYLLABLE_R;
	}
	else if (twist == TWIST_R2)
	{
		if ((syllable == SYLLABLE_R) || (syllable == SYLLABLE_R2) ||
			(syllable == SYLLABLE_R3) ||
			(syllable == SYLLABLE_L) || (syllable == SYLLABLE_L2) ||
			(syllable == SYLLABLE_L3) ||
			(syllable == SYLLABLE_RL) || (syllable == SYLLABLE_RL2) ||
			(syllable == SYLLABLE_RL3) || (syllable == SYLLABLE_R2L2))
			return SYLLABLE_INVALID;
		else
			return SYLLABLE_R2;
	}
	else if (twist == TWIST_R3)
	{
		if ((syllable == SYLLABLE_R) || (syllable == SYLLABLE_R2) ||
			(syllable == SYLLABLE_R3) ||
			(syllable == SYLLABLE_L) || (syllable == SYLLABLE_L2) ||
			(syllable == SYLLABLE_L3) ||
			(syllable == SYLLABLE_RL) || (syllable == SYLLABLE_RL2) ||
			(syllable == SYLLABLE_RL3) || (syllable == SYLLABLE_R2L2))
			return SYLLABLE_INVALID;
		else
			return SYLLABLE_R3;
	}
	else if (twist == TWIST_U)
	{
		if ((syllable == SYLLABLE_U) || (syllable == SYLLABLE_U2) ||
			(syllable == SYLLABLE_U3) ||
			(syllable == SYLLABLE_D) || (syllable == SYLLABLE_D2) ||
			(syllable == SYLLABLE_D3) ||
			(syllable == SYLLABLE_UD) || (syllable == SYLLABLE_UD2) ||
			(syllable == SYLLABLE_UD3) || (syllable == SYLLABLE_U2D2))
			return SYLLABLE_INVALID;
		else
			return SYLLABLE_U;
	}
	else if (twist == TWIST_U2)
	{
		if ((syllable == SYLLABLE_U) || (syllable == SYLLABLE_U2) ||
			(syllable == SYLLABLE_U3) ||
			(syllable == SYLLABLE_D) || (syllable == SYLLABLE_D2) ||
			(syllable == SYLLABLE_D3) ||
			(syllable == SYLLABLE_UD) || (syllable == SYLLABLE_UD2) ||
			(syllable == SYLLABLE_UD3) || (syllable == SYLLABLE_U2D2))
			return SYLLABLE_INVALID;
		else
			return SYLLABLE_U2;
	}
	else if (twist == TWIST_U3)
	{
		if ((syllable == SYLLABLE_U) || (syllable == SYLLABLE_U2) ||
			(syllable == SYLLABLE_U3) ||
			(syllable == SYLLABLE_D) || (syllable == SYLLABLE_D2) ||
			(syllable == SYLLABLE_D3) ||
			(syllable == SYLLABLE_UD) || (syllable == SYLLABLE_UD2) ||
			(syllable == SYLLABLE_UD3) || (syllable == SYLLABLE_U2D2))
			return SYLLABLE_INVALID;
		else
			return SYLLABLE_U3;
	}
	else if (twist == TWIST_B)
	{
		if ((syllable == SYLLABLE_B) || (syllable == SYLLABLE_B2) ||
			(syllable == SYLLABLE_B3) ||
			(syllable == SYLLABLE_FB) || (syllable == SYLLABLE_FB2) ||
			(syllable == SYLLABLE_FB3) || (syllable == SYLLABLE_F2B2))
			return SYLLABLE_INVALID;
		else if (syllable == SYLLABLE_F)
			return SYLLABLE_FB;
		else if (syllable == SYLLABLE_F2)
			return SYLLABLE_FB2;
		else if (syllable == SYLLABLE_F3)
			return SYLLABLE_FB3;
		else
			return SYLLABLE_B;
	}
	else if (twist == TWIST_B2)
	{
		if ((syllable == SYLLABLE_B) || (syllable == SYLLABLE_B2) ||
			(syllable == SYLLABLE_B3) ||
			(syllable == SYLLABLE_FB) || (syllable == SYLLABLE_FB2) ||
			(syllable == SYLLABLE_FB3) || (syllable == SYLLABLE_F2B2))
			return SYLLABLE_INVALID;
		else if ((syllable == SYLLABLE_F) || (syllable == SYLLABLE_F3))
			return SYLLABLE_FB2;
		else if (syllable == SYLLABLE_F2)
			return SYLLABLE_F2B2;
		else
			return SYLLABLE_B2;
	}
	else if (twist == TWIST_B3)
	{
		if ((syllable == SYLLABLE_B) || (syllable == SYLLABLE_B2) ||
			(syllable == SYLLABLE_B3) ||
			(syllable == SYLLABLE_FB) || (syllable == SYLLABLE_FB2) ||
			(syllable == SYLLABLE_FB3) || (syllable == SYLLABLE_F2B2))
			return SYLLABLE_INVALID;
		else if (syllable == SYLLABLE_F)
			return SYLLABLE_FB3;
		else if (syllable == SYLLABLE_F2)
			return SYLLABLE_FB2;
		else if (syllable == SYLLABLE_F3)
			return SYLLABLE_FB;
		else
			return SYLLABLE_B3;
	}
	else if (twist == TWIST_L)
	{
		if ((syllable == SYLLABLE_L) || (syllable == SYLLABLE_L2) ||
			(syllable == SYLLABLE_L3) ||
			(syllable == SYLLABLE_RL) || (syllable == SYLLABLE_RL2) ||
			(syllable == SYLLABLE_RL3) || (syllable == SYLLABLE_R2L2))
			return SYLLABLE_INVALID;
		else if (syllable == SYLLABLE_R)
			return SYLLABLE_RL;
		else if (syllable == SYLLABLE_R2)
			return SYLLABLE_RL2;
		else if (syllable == SYLLABLE_R3)
			return SYLLABLE_RL3;
		else
			return SYLLABLE_L;
	}
	else if (twist == TWIST_L2)
	{
		if ((syllable == SYLLABLE_L) || (syllable == SYLLABLE_L2) ||
			(syllable == SYLLABLE_L3) ||
			(syllable == SYLLABLE_RL) || (syllable == SYLLABLE_RL2) ||
			(syllable == SYLLABLE_RL3) || (syllable == SYLLABLE_R2L2))
			return SYLLABLE_INVALID;
		else if ((syllable == SYLLABLE_R) || (syllable == SYLLABLE_R3))
			return SYLLABLE_RL2;
		else if (syllable == SYLLABLE_R2)
			return SYLLABLE_R2L2;
		else
			return SYLLABLE_L2;
	}
	else if (twist == TWIST_L3)
	{
		if ((syllable == SYLLABLE_L) || (syllable == SYLLABLE_L2) ||
			(syllable == SYLLABLE_L3) ||
			(syllable == SYLLABLE_RL) || (syllable == SYLLABLE_RL2) ||
			(syllable == SYLLABLE_RL3) || (syllable == SYLLABLE_R2L2))
			return SYLLABLE_INVALID;
		else if (syllable == SYLLABLE_R)
			return SYLLABLE_RL3;
		else if (syllable == SYLLABLE_R2)
			return SYLLABLE_RL2;
		else if (syllable == SYLLABLE_R3)
			return SYLLABLE_RL;
		else
			return SYLLABLE_L3;
	}
	else if (twist == TWIST_D)
	{
		if ((syllable == SYLLABLE_D) || (syllable == SYLLABLE_D2) ||
			(syllable == SYLLABLE_D3) ||
			(syllable == SYLLABLE_UD) || (syllable == SYLLABLE_UD2) ||
			(syllable == SYLLABLE_UD3) || (syllable == SYLLABLE_U2D2))
			return SYLLABLE_INVALID;
		else if (syllable == SYLLABLE_U)
			return SYLLABLE_UD;
		else if (syllable == SYLLABLE_U2)
			return SYLLABLE_UD2;
		else if (syllable == SYLLABLE_U3)
			return SYLLABLE_UD3;
		else
			return SYLLABLE_D;
	}
	else if (twist == TWIST_D2)
	{
		if ((syllable == SYLLABLE_D) || (syllable == SYLLABLE_D2) ||
			(syllable == SYLLABLE_D3) ||
			(syllable == SYLLABLE_UD) || (syllable == SYLLABLE_UD2) ||
			(syllable == SYLLABLE_UD3) || (syllable == SYLLABLE_U2D2))
			return SYLLABLE_INVALID;
		else if ((syllable == SYLLABLE_U) || (syllable == SYLLABLE_U3))
			return SYLLABLE_UD2;
		else if (syllable == SYLLABLE_U2)
			return SYLLABLE_U2D2;
		else
			return SYLLABLE_D2;
	}
	else if (twist == TWIST_D3)
	{
		if ((syllable == SYLLABLE_D) || (syllable == SYLLABLE_D2) ||
			(syllable == SYLLABLE_D3) ||
			(syllable == SYLLABLE_UD) || (syllable == SYLLABLE_UD2) ||
			(syllable == SYLLABLE_UD3) || (syllable == SYLLABLE_U2D2))
			return SYLLABLE_INVALID;
		else if (syllable == SYLLABLE_U)
			return SYLLABLE_UD3;
		else if (syllable == SYLLABLE_U2)
			return SYLLABLE_UD2;
		else if (syllable == SYLLABLE_U3)
			return SYLLABLE_UD;
		else
			return SYLLABLE_D3;
	}
	else
		exit_w_error_message("twist_on_syllable : invalid twist");

	return SYLLABLE_INVALID;
}


/* ========================================================================= */
int  not_minimal_one_twist(int  subgroup[N_CUBESYM],
									int  sym_on_twist[N_CUBESYM][N_TWIST],
									int  twist)
									/* ------------------------------------------------------------------------- */

{
	int                     sym;


	for (sym = 0; sym < N_CUBESYM; sym++)
		if (subgroup[sym] && (sym_on_twist[sym][twist] < twist))
			return 1;

	return 0;
}


/* ========================================================================= */
int  not_minimal_two_twists(int  subgroup[N_CUBESYM],
									 int  sym_on_twist[N_CUBESYM][N_TWIST],
									 int  twist0, int  twist1)
									 /* ------------------------------------------------------------------------- */

{
	int                     twist_arr[2], sym;


	for (sym = 0; sym < N_CUBESYM; sym++)
		if (subgroup[sym])
		{
			twist_arr[0] = sym_on_twist[sym][twist0];
			twist_arr[1] = sym_on_twist[sym][twist1];
			clean_up_sequence(twist_arr, 2);
			if ((twist_arr[0] < twist0) ||
				((twist_arr[0] == twist0) && (twist_arr[1] < twist1)))
				return 1;
		}

		return 0;
}


/* ========================================================================= */
void  calc_twist_on_sylsubgrp(int
										tw_on_sylsubgrp[N_TWIST][N_SYLLABLE * N_SYMSUBGRP])
										/* ------------------------------------------------------------------------- */

{
	int             subgroup_list[N_SYMSUBGRP][N_CUBESYM];
	int             sym_on_twist[N_CUBESYM][N_TWIST];
	int             syllable_on_subgrp[N_SYLLABLE][N_SYMSUBGRP];
	int             syllable, subgrp, subgrp_before, subgrp_after, twist;
	int             new_syllable, new_subgrp;


	calc_subgroup_list(subgroup_list);
	calc_sym_on_twist(sym_on_twist);
	calc_syllable_on_sym(subgroup_list, sym_on_twist, syllable_on_subgrp);

	for (syllable = 0; syllable < N_SYLLABLE; syllable++)
		for (subgrp = 0; subgrp < N_SYMSUBGRP; subgrp++)
		{
			if (is_single_twist(syllable))
			{
				subgrp_before = subgrp;
				subgrp_after = syllable_on_subgrp[syllable][subgrp];
			}
			else
			{
				subgrp_before = SUBGRP_TRIVIAL;

				if (syllable == SYLLABLE_INVALID)
					subgrp_after = SUBGRP_TRIVIAL;
				else
					subgrp_after = subgrp;
			}

			for (twist = 0; twist < N_TWIST; twist++)
			{
				new_syllable = twist_on_syllable(twist, syllable);

				if (is_single_twist(new_syllable))
				{
					if (not_minimal_one_twist(subgroup_list[subgrp_after],
						sym_on_twist, syllable_to_twist(new_syllable)))
						new_syllable = SYLLABLE_INVALID;
				}
				else if (new_syllable != SYLLABLE_INVALID)
				{
					if (not_minimal_two_twists(subgroup_list[subgrp_before],
						sym_on_twist, syllable_to_twist(syllable), twist))
						new_syllable = SYLLABLE_INVALID;
				}

				if (new_syllable == SYLLABLE_INVALID)
					new_subgrp = SUBGRP_TRIVIAL;
				else if (is_single_twist(new_syllable))
					new_subgrp = subgrp_after;
				else
					new_subgrp = syllable_on_subgrp[new_syllable][subgrp_before];

				tw_on_sylsubgrp[twist][syllable * N_SYMSUBGRP + subgrp] =
					new_syllable * N_SYMSUBGRP + new_subgrp;
			}
		}

		return;
}


/* ========================================================================= */
void  init_twist_on_follow(void)
/* ------------------------------------------------------------------------- */

{
	unsigned short        (*mem_ptr)[N_FOLLOW];
	int                     tw_on_sylsubgrp[N_TWIST][N_SYLLABLE * N_SYMSUBGRP];
	int                     occurs[N_SYLLABLE * N_SYMSUBGRP];
	int                     sylsubgrp_to_follow[N_SYLLABLE * N_SYMSUBGRP];
	int                     follow_to_sylsubgrp[N_FOLLOW];
	int                     syllable, subgrp, found, twist, count, follow;


	calc_twist_on_sylsubgrp(tw_on_sylsubgrp);

	for (syllable = 0; syllable < N_SYLLABLE; syllable++)
		for (subgrp = 0; subgrp < N_SYMSUBGRP; subgrp++)
			occurs[syllable * N_SYMSUBGRP + subgrp] = 0;

	for (subgrp = 0; subgrp < N_SYMSUBGRP; subgrp++)
		occurs[SYLLABLE_NONE * N_SYMSUBGRP + subgrp] = 1;

	found = 1;

	while (found)
	{
		found = 0;
		for (syllable = 0; syllable < N_SYLLABLE; syllable++)
			for (subgrp = 0; subgrp < N_SYMSUBGRP; subgrp++)
				for (twist = 0; twist < N_TWIST; twist++)
					if (occurs[tw_on_sylsubgrp[twist]
					[syllable * N_SYMSUBGRP + subgrp]] == 0)
					{
						occurs[tw_on_sylsubgrp[twist]
						[syllable * N_SYMSUBGRP + subgrp]] = 1;
						found = 1;
					}
	}

	count = 0;

	for (syllable = 0; syllable < N_SYLLABLE; syllable++)
		for (subgrp = 0; subgrp < N_SYMSUBGRP; subgrp++)
		{
			if (occurs[syllable * N_SYMSUBGRP + subgrp] == 0)
				sylsubgrp_to_follow[syllable * N_SYMSUBGRP + subgrp] = -1;
			else
			{
				sylsubgrp_to_follow[syllable * N_SYMSUBGRP + subgrp] = count;
				follow_to_sylsubgrp[count] = syllable * N_SYMSUBGRP + subgrp;
				count++;
			}
		}

		if (count != N_FOLLOW)
			exit_w_error_message("init_twist_on_follow : wrong number of  follow's");

		for (subgrp = 0; subgrp < N_SYMSUBGRP; subgrp++)
			if (sylsubgrp_to_follow[SYLLABLE_NONE * N_SYMSUBGRP + subgrp]
			!= 1 + subgrp)
				exit_w_error_message("init_twist_on_follow : indexing error");

			mem_ptr = (unsigned short (*)[N_FOLLOW])
				malloc(sizeof(unsigned short [N_TWIST][N_FOLLOW]));
			if (mem_ptr == NULL)
				exit_w_error_message("init_twist_on_follow : couldn't get memory");

			for (twist = 0; twist < N_TWIST; twist++)
				twist_on_follow[twist] = mem_ptr[twist];

			for (twist = 0; twist < N_TWIST; twist++)
				for (follow = 0; follow < N_FOLLOW; follow++)
					twist_on_follow[twist][follow] =
					(unsigned short)sylsubgrp_to_follow[tw_on_sylsubgrp[twist]
				[follow_to_sylsubgrp[follow]]];

				return;
}


/* ========================================================================= */
void  corner_unpack(int  corner, int  array_out[8])
/* ------------------------------------------------------------------------- */

/*  input:  corner
output:  array_out[]  */

{
	int                     ii;


	for (ii = 0; ii < 7; ii++)
	{
		array_out[ii] = corner % 3;
		corner = corner / 3;
	}

	array_out[7] = (2 * (array_out[0] + array_out[1] + array_out[2] + array_out[3]
	+ array_out[4] + array_out[5] + array_out[6])) % 3;

	return;
}


/* ========================================================================= */
int  corner_pack(int  array_in[8])
/* ------------------------------------------------------------------------- */

/*  input:  array_in[]  */

{
	int                     corner, ii;


	corner = 0;
	for (ii = 6; ii >= 0; ii--)
		corner = 3 * corner + array_in[ii];

	return corner;
}


/* ========================================================================= */
void  corner_adjust(int  array[8], int  ind0, int  ind1, int  ind2,
						  int  ind3)
						  /* ------------------------------------------------------------------------- */

{
	array[ind0] = (array[ind0] + 1) % 3;
	array[ind1] = (array[ind1] + 2) % 3;
	array[ind2] = (array[ind2] + 1) % 3;
	array[ind3] = (array[ind3] + 2) % 3;

	return;
}


/* ========================================================================= */
int  twist_f_on_corner(int  corner)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	corner_unpack(corner, temp_arr);
	four_cycle(temp_arr, CORNER_DFL, CORNER_DRF, CORNER_UFR, CORNER_ULF);
	corner_adjust(temp_arr, CORNER_DFL, CORNER_DRF, CORNER_UFR, CORNER_ULF);

	return corner_pack(temp_arr);
}


/* ========================================================================= */
int  twist_r_on_corner(int  corner)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	corner_unpack(corner, temp_arr);
	four_cycle(temp_arr, CORNER_DRF, CORNER_DBR, CORNER_URB, CORNER_UFR);
	corner_adjust(temp_arr, CORNER_DRF, CORNER_DBR, CORNER_URB, CORNER_UFR);

	return corner_pack(temp_arr);
}


/* ========================================================================= */
int  twist_u_on_corner(int  corner)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	corner_unpack(corner, temp_arr);
	four_cycle(temp_arr, CORNER_UFR, CORNER_URB, CORNER_UBL, CORNER_ULF);

	return corner_pack(temp_arr);
}


/* ========================================================================= */
int  twist_b_on_corner(int  corner)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	corner_unpack(corner, temp_arr);
	four_cycle(temp_arr, CORNER_DBR, CORNER_DLB, CORNER_UBL, CORNER_URB);
	corner_adjust(temp_arr, CORNER_DBR, CORNER_DLB, CORNER_UBL, CORNER_URB);

	return corner_pack(temp_arr);
}


/* ========================================================================= */
int  twist_l_on_corner(int  corner)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	corner_unpack(corner, temp_arr);
	four_cycle(temp_arr, CORNER_DLB, CORNER_DFL, CORNER_ULF, CORNER_UBL);
	corner_adjust(temp_arr, CORNER_DLB, CORNER_DFL, CORNER_ULF, CORNER_UBL);

	return corner_pack(temp_arr);
}


/* ========================================================================= */
int  twist_d_on_corner(int  corner)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	corner_unpack(corner, temp_arr);
	four_cycle(temp_arr, CORNER_DFL, CORNER_DLB, CORNER_DBR, CORNER_DRF);

	return corner_pack(temp_arr);
}


/* ========================================================================= */
void  init_twist_on_corner(void)
/* ------------------------------------------------------------------------- */

{
	int                     twist, corner;


	/*  allocate and initialize global array   twist_on_corner    */

	twist_on_corner[0] =
		(unsigned short *)malloc(sizeof(unsigned short [N_TWIST][N_CORNER]));
	if (twist_on_corner[0] == NULL)
		exit_w_error_message("init_twist_on_corner : couldn't get memory");

	for (twist = 1; twist < N_TWIST; twist++)
		twist_on_corner[twist] = &twist_on_corner[0][twist * N_CORNER];

	for (corner = 0; corner < N_CORNER; corner++)
	{
		twist_on_corner[TWIST_F][corner] =
			(unsigned short)twist_f_on_corner(corner);
		twist_on_corner[TWIST_R][corner] =
			(unsigned short)twist_r_on_corner(corner);
		twist_on_corner[TWIST_U][corner] =
			(unsigned short)twist_u_on_corner(corner);
		twist_on_corner[TWIST_B][corner] =
			(unsigned short)twist_b_on_corner(corner);
		twist_on_corner[TWIST_L][corner] =
			(unsigned short)twist_l_on_corner(corner);
		twist_on_corner[TWIST_D][corner] =
			(unsigned short)twist_d_on_corner(corner);
	}
	for (corner = 0; corner < N_CORNER; corner++)
	{
		twist_on_corner[TWIST_F2][corner] =
			twist_on_corner[TWIST_F][(int)twist_on_corner[TWIST_F][corner]];
		twist_on_corner[TWIST_R2][corner] =
			twist_on_corner[TWIST_R][(int)twist_on_corner[TWIST_R][corner]];
		twist_on_corner[TWIST_U2][corner] =
			twist_on_corner[TWIST_U][(int)twist_on_corner[TWIST_U][corner]];
		twist_on_corner[TWIST_B2][corner] =
			twist_on_corner[TWIST_B][(int)twist_on_corner[TWIST_B][corner]];
		twist_on_corner[TWIST_L2][corner] =
			twist_on_corner[TWIST_L][(int)twist_on_corner[TWIST_L][corner]];
		twist_on_corner[TWIST_D2][corner] =
			twist_on_corner[TWIST_D][(int)twist_on_corner[TWIST_D][corner]];
	}
	for (corner = 0; corner < N_CORNER; corner++)
	{
		twist_on_corner[TWIST_F3][corner] =
			twist_on_corner[TWIST_F2][(int)twist_on_corner[TWIST_F][corner]];
		twist_on_corner[TWIST_R3][corner] =
			twist_on_corner[TWIST_R2][(int)twist_on_corner[TWIST_R][corner]];
		twist_on_corner[TWIST_U3][corner] =
			twist_on_corner[TWIST_U2][(int)twist_on_corner[TWIST_U][corner]];
		twist_on_corner[TWIST_B3][corner] =
			twist_on_corner[TWIST_B2][(int)twist_on_corner[TWIST_B][corner]];
		twist_on_corner[TWIST_L3][corner] =
			twist_on_corner[TWIST_L2][(int)twist_on_corner[TWIST_L][corner]];
		twist_on_corner[TWIST_D3][corner] =
			twist_on_corner[TWIST_D2][(int)twist_on_corner[TWIST_D][corner]];
	}

	return;
}


/* ========================================================================= */
int  sym_cu_on_corner(int  corner)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	corner_unpack(corner, temp_arr);
	four_cycle(temp_arr, CORNER_UFR, CORNER_URB, CORNER_UBL, CORNER_ULF);
	four_cycle(temp_arr, CORNER_DRF, CORNER_DBR, CORNER_DLB, CORNER_DFL);

	return corner_pack(temp_arr);
}


/* ========================================================================= */
int  sym_cf2_on_corner(int  corner)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	corner_unpack(corner, temp_arr);
	two_cycle(temp_arr, CORNER_UFR, CORNER_DFL);
	two_cycle(temp_arr, CORNER_ULF, CORNER_DRF);
	two_cycle(temp_arr, CORNER_UBL, CORNER_DBR);
	two_cycle(temp_arr, CORNER_URB, CORNER_DLB);

	return corner_pack(temp_arr);
}


/* ========================================================================= */
int  sym_rud_on_corner(int  corner)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8], ii;


	corner_unpack(corner, temp_arr);
	two_cycle(temp_arr, CORNER_UFR, CORNER_DRF);
	two_cycle(temp_arr, CORNER_ULF, CORNER_DFL);
	two_cycle(temp_arr, CORNER_UBL, CORNER_DLB);
	two_cycle(temp_arr, CORNER_URB, CORNER_DBR);

	for (ii = 0; ii < 8; ii++)
		temp_arr[ii] = (2 * temp_arr[ii]) % 3;

	return corner_pack(temp_arr);
}


/* ========================================================================= */
void  init_sym_on_corner(void)
/* ------------------------------------------------------------------------- */

{
	int                     corner, sym;


	/*  allocate and initialize global array   sym_on_corner    */

	sym_on_corner[0] =
		(unsigned short *)malloc(sizeof(unsigned short [N_SYM][N_CORNER]));
	if (sym_on_corner[0] == NULL)
		exit_w_error_message("init_sym_on_corner : couldn't get memory");

	for (sym = 1; sym < N_SYM; sym++)
		sym_on_corner[sym] = &sym_on_corner[0][sym * N_CORNER];

	for (corner = 0; corner < N_CORNER; corner++)
		sym_on_corner[0][corner] = (unsigned short)corner;

	for (corner = 0; corner < N_CORNER; corner++)
		sym_on_corner[1][corner] = (unsigned short)sym_cu_on_corner(corner);

	for (sym = 2; sym < 4; sym++)
		for (corner = 0; corner < N_CORNER; corner++)
			sym_on_corner[sym][corner] =
			sym_on_corner[1][(int)sym_on_corner[sym - 1][corner]];

	for (corner = 0; corner < N_CORNER; corner++)
		sym_on_corner[4][corner] = (unsigned short)sym_cf2_on_corner(corner);

	for (sym = 5; sym < 8; sym++)
		for (corner = 0; corner < N_CORNER; corner++)
			sym_on_corner[sym][corner] =
			sym_on_corner[4][(int)sym_on_corner[sym - 4][corner]];

	for (corner = 0; corner < N_CORNER; corner++)
		sym_on_corner[8][corner] = (unsigned short)sym_rud_on_corner(corner);

	for (sym = 9; sym < 16; sym++)
		for (corner = 0; corner < N_CORNER; corner++)
			sym_on_corner[sym][corner] =
			sym_on_corner[8][(int)sym_on_corner[sym - 8][corner]];

	return;
}


/* ========================================================================= */
void  calc_edgeloc_conv(int  conv_tab[N_ELOC], int  unconv_tab[N_ELOC_CONV])
/* ------------------------------------------------------------------------- */

{
	int                     ii, loc0, loc1, loc2, loc3, count;


	for (ii = 0; ii < N_ELOC; ii++)
		conv_tab[ii] = -1;

	for (ii = 0; ii < N_ELOC_CONV; ii++)
		unconv_tab[ii] = -1;

	count = 0;
	for (loc0 = 0; loc0 < 9; loc0++)
		for (loc1 = loc0 + 1; loc1 < 10; loc1++)
			for (loc2 = loc1 + 1; loc2 < 11; loc2++)
				for (loc3 = loc2 + 1; loc3 < 12; loc3++)
				{
					if (count >= N_ELOC)
						exit_w_error_message("calc_edgeloc_conv : too many eloc's");
					conv_tab[count] = (1 << loc0) | (1 << loc1) |
						(1 << loc2) | (1 << loc3);
					unconv_tab[conv_tab[count]] = count;
					count++;
				}

				if (count != N_ELOC)
					exit_w_error_message("calc_edgeloc_conv : wrong number of  eloc's");

				return;
}


/* ========================================================================= */
int  edgeloc_conv_or_unconv(int  eloc_conv_or_unconv, int  convert_flag)
/* ------------------------------------------------------------------------- */

{
	static int              eloc_conv[N_ELOC];
	static int              eloc_unconv[N_ELOC_CONV];
	static int              initialized = 0;
	int                     el_conv, el_unconv;


	if (initialized == 0)
	{
		calc_edgeloc_conv(eloc_conv, eloc_unconv);
		initialized = 1;
	}

	if (convert_flag)
	{
		if ((eloc_conv_or_unconv < 0) || (eloc_conv_or_unconv >= N_ELOC))
			exit_w_error_message("edgeloc_conv_or_unconv : invalid input");

		el_conv = eloc_conv[eloc_conv_or_unconv];

		if (el_conv < 0)
			exit_w_error_message("edgeloc_conv_or_unconv : corrupted data");

		return el_conv;
	}
	else
	{
		if ((eloc_conv_or_unconv < 0) || (eloc_conv_or_unconv >= N_ELOC_CONV))
			exit_w_error_message("edgeloc_conv_or_unconv : invalid input");

		el_unconv = eloc_unconv[eloc_conv_or_unconv];

		if (el_unconv < 0)
			exit_w_error_message("edgeloc_conv_or_unconv : corrupted data");

		return el_unconv;
	}
}


/* ========================================================================= */
int  edgeloc_conv(int  eloc_unconv)
/* ------------------------------------------------------------------------- */

{
	return edgeloc_conv_or_unconv(eloc_unconv, 1);
}


/* ========================================================================= */
int  edgeloc_unconv(int  eloc_conv)
/* ------------------------------------------------------------------------- */

{
	return edgeloc_conv_or_unconv(eloc_conv, 0);
}


/* ========================================================================= */
void  eloc_unpack(int  eloc, int  array_out[12])
/* ------------------------------------------------------------------------- */

/*  input:  eloc
output:  array_out[]  */

{
	int                     conv, ii;


	conv = edgeloc_conv(eloc);

	for (ii = 0; ii < 12; ii++)
	{
		array_out[ii] = conv % 2;
		conv = conv / 2;
	}

	return;
}


/* ========================================================================= */
int  eloc_pack(int  array_in[12])
/* ------------------------------------------------------------------------- */

{
	int                     ii, conv;


	conv = 0;
	for (ii = 11; ii >= 0; ii--)
		conv = 2 * conv + array_in[ii];

	return edgeloc_unconv(conv);
}


/* ========================================================================= */
int  twist_f_on_eloc(int  eloc)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eloc_unpack(eloc, temp_arr);
	four_cycle(temp_arr, EDGE_FL, EDGE_DF, EDGE_FR, EDGE_UF);

	return eloc_pack(temp_arr);
}


/* ========================================================================= */
int  twist_r_on_eloc(int  eloc)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eloc_unpack(eloc, temp_arr);
	four_cycle(temp_arr, EDGE_FR, EDGE_DR, EDGE_BR, EDGE_UR);

	return eloc_pack(temp_arr);
}


/* ========================================================================= */
int  twist_u_on_eloc(int  eloc)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eloc_unpack(eloc, temp_arr);
	four_cycle(temp_arr, EDGE_UF, EDGE_UR, EDGE_UB, EDGE_UL);

	return eloc_pack(temp_arr);
}


/* ========================================================================= */
int  twist_b_on_eloc(int  eloc)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eloc_unpack(eloc, temp_arr);
	four_cycle(temp_arr, EDGE_BR, EDGE_DB, EDGE_BL, EDGE_UB);

	return eloc_pack(temp_arr);
}


/* ========================================================================= */
int  twist_l_on_eloc(int  eloc)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eloc_unpack(eloc, temp_arr);
	four_cycle(temp_arr, EDGE_BL, EDGE_DL, EDGE_FL, EDGE_UL);

	return eloc_pack(temp_arr);
}


/* ========================================================================= */
int  twist_d_on_eloc(int  eloc)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eloc_unpack(eloc, temp_arr);
	four_cycle(temp_arr, EDGE_DF, EDGE_DL, EDGE_DB, EDGE_DR);

	return eloc_pack(temp_arr);
}


/* ========================================================================= */
void  calc_twist_on_eloc(int  table[N_TWIST][N_ELOC])
/* ------------------------------------------------------------------------- */

{
	int                     edgeloc;


	for (edgeloc = 0; edgeloc < N_ELOC; edgeloc++)
	{
		table[TWIST_F][edgeloc] = twist_f_on_eloc(edgeloc);
		table[TWIST_R][edgeloc] = twist_r_on_eloc(edgeloc);
		table[TWIST_U][edgeloc] = twist_u_on_eloc(edgeloc);
		table[TWIST_B][edgeloc] = twist_b_on_eloc(edgeloc);
		table[TWIST_L][edgeloc] = twist_l_on_eloc(edgeloc);
		table[TWIST_D][edgeloc] = twist_d_on_eloc(edgeloc);
	}

	perm_n_compose(N_ELOC, table[TWIST_F], table[TWIST_F], table[TWIST_F2]);
	perm_n_compose(N_ELOC, table[TWIST_R], table[TWIST_R], table[TWIST_R2]);
	perm_n_compose(N_ELOC, table[TWIST_U], table[TWIST_U], table[TWIST_U2]);
	perm_n_compose(N_ELOC, table[TWIST_B], table[TWIST_B], table[TWIST_B2]);
	perm_n_compose(N_ELOC, table[TWIST_L], table[TWIST_L], table[TWIST_L2]);
	perm_n_compose(N_ELOC, table[TWIST_D], table[TWIST_D], table[TWIST_D2]);

	perm_n_compose(N_ELOC, table[TWIST_F2], table[TWIST_F], table[TWIST_F3]);
	perm_n_compose(N_ELOC, table[TWIST_R2], table[TWIST_R], table[TWIST_R3]);
	perm_n_compose(N_ELOC, table[TWIST_U2], table[TWIST_U], table[TWIST_U3]);
	perm_n_compose(N_ELOC, table[TWIST_B2], table[TWIST_B], table[TWIST_B3]);
	perm_n_compose(N_ELOC, table[TWIST_L2], table[TWIST_L], table[TWIST_L3]);
	perm_n_compose(N_ELOC, table[TWIST_D2], table[TWIST_D], table[TWIST_D3]);

	return;
}


/* ========================================================================= */
int  sym_cu_on_eloc(int  eloc)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eloc_unpack(eloc, temp_arr);
	four_cycle(temp_arr, EDGE_UF, EDGE_UR, EDGE_UB, EDGE_UL);
	four_cycle(temp_arr, EDGE_DF, EDGE_DR, EDGE_DB, EDGE_DL);
	four_cycle(temp_arr, EDGE_FR, EDGE_BR, EDGE_BL, EDGE_FL);

	return eloc_pack(temp_arr);
}


/* ========================================================================= */
int  sym_cf2_on_eloc(int  eloc)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eloc_unpack(eloc, temp_arr);
	two_cycle(temp_arr, EDGE_UF, EDGE_DF);
	two_cycle(temp_arr, EDGE_UR, EDGE_DL);
	two_cycle(temp_arr, EDGE_UB, EDGE_DB);
	two_cycle(temp_arr, EDGE_UL, EDGE_DR);
	two_cycle(temp_arr, EDGE_FR, EDGE_FL);
	two_cycle(temp_arr, EDGE_BR, EDGE_BL);

	return eloc_pack(temp_arr);
}


/* ========================================================================= */
int  sym_rud_on_eloc(int  eloc)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eloc_unpack(eloc, temp_arr);
	two_cycle(temp_arr, EDGE_UF, EDGE_DF);
	two_cycle(temp_arr, EDGE_UR, EDGE_DR);
	two_cycle(temp_arr, EDGE_UB, EDGE_DB);
	two_cycle(temp_arr, EDGE_UL, EDGE_DL);

	return eloc_pack(temp_arr);
}


/* ========================================================================= */
void  calc_sym_on_eloc(int  table[N_SYM][N_ELOC])
/* ------------------------------------------------------------------------- */

{
	int                     edgeloc, sym;


	perm_n_init(N_ELOC, table[0]);

	for (edgeloc = 0; edgeloc < N_ELOC; edgeloc++)
		table[1][edgeloc] = sym_cu_on_eloc(edgeloc);

	for (sym = 2; sym < 4; sym++)
		perm_n_compose(N_ELOC, table[1], table[sym - 1], table[sym]);

	for (edgeloc = 0; edgeloc < N_ELOC; edgeloc++)
		table[4][edgeloc] = sym_cf2_on_eloc(edgeloc);

	for (sym = 5; sym < 8; sym++)
		perm_n_compose(N_ELOC, table[4], table[sym - 4], table[sym]);

	for (edgeloc = 0; edgeloc < N_ELOC; edgeloc++)
		table[8][edgeloc] = sym_rud_on_eloc(edgeloc);

	for (sym = 9; sym < 16; sym++)
		perm_n_compose(N_ELOC, table[8], table[sym - 8], table[sym]);

	return;
}


/* ========================================================================= */
void  eflip_unpack(int  eflip, int  array_out[12])
/* ------------------------------------------------------------------------- */

{
	int                     ii;


	for (ii = 0; ii < 11; ii++)
	{
		array_out[ii] = eflip % 2;
		eflip = eflip / 2;
	}

	array_out[11] = (array_out[0] + array_out[1] + array_out[2] + array_out[3] +
		array_out[4] + array_out[5] + array_out[6] + array_out[7] +
		array_out[8] + array_out[9] + array_out[10]) % 2;

	return;
}


/* ========================================================================= */
int  eflip_pack(int  array_in[12])
/* ------------------------------------------------------------------------- */

{
	int                     eflip, ii;


	eflip = 0;
	for (ii = 10; ii >= 0; ii--)
		eflip = 2 * eflip + array_in[ii];

	return eflip;
}


/* ========================================================================= */
void  eflip_adjust(int  array[12], int  ind0, int  ind1, int  ind2,
						 int  ind3)
						 /* ------------------------------------------------------------------------- */

{
	array[ind0] = 1 - array[ind0];
	array[ind1] = 1 - array[ind1];
	array[ind2] = 1 - array[ind2];
	array[ind3] = 1 - array[ind3];

	return;
}


/* ========================================================================= */
int  twist_f_on_eflip(int  eflip)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eflip_unpack(eflip, temp_arr);
	four_cycle(temp_arr, EDGE_FL, EDGE_DF, EDGE_FR, EDGE_UF);
	eflip_adjust(temp_arr, EDGE_FL, EDGE_DF, EDGE_FR, EDGE_UF);

	return eflip_pack(temp_arr);
}


/* ========================================================================= */
int  twist_r_on_eflip(int  eflip)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eflip_unpack(eflip, temp_arr);
	four_cycle(temp_arr, EDGE_FR, EDGE_DR, EDGE_BR, EDGE_UR);

	return eflip_pack(temp_arr);
}


/* ========================================================================= */
int  twist_u_on_eflip(int  eflip)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eflip_unpack(eflip, temp_arr);
	four_cycle(temp_arr, EDGE_UR, EDGE_UB, EDGE_UL, EDGE_UF);

	return eflip_pack(temp_arr);
}


/* ========================================================================= */
int  twist_b_on_eflip(int  eflip)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eflip_unpack(eflip, temp_arr);
	four_cycle(temp_arr, EDGE_BR, EDGE_DB, EDGE_BL, EDGE_UB);
	eflip_adjust(temp_arr, EDGE_BR, EDGE_DB, EDGE_BL, EDGE_UB);

	return eflip_pack(temp_arr);
}


/* ========================================================================= */
int  twist_l_on_eflip(int  eflip)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eflip_unpack(eflip, temp_arr);
	four_cycle(temp_arr, EDGE_BL, EDGE_DL, EDGE_FL, EDGE_UL);

	return eflip_pack(temp_arr);
}


/* ========================================================================= */
int  twist_d_on_eflip(int  eflip)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eflip_unpack(eflip, temp_arr);
	four_cycle(temp_arr, EDGE_DL, EDGE_DB, EDGE_DR, EDGE_DF);

	return eflip_pack(temp_arr);
}


/* ========================================================================= */
void  calc_twist_on_eflip(int  table[N_TWIST][N_EFLIP])
/* ------------------------------------------------------------------------- */

{
	int                     edgeflip;


	for (edgeflip = 0; edgeflip < N_EFLIP; edgeflip++)
	{
		table[TWIST_F][edgeflip] = twist_f_on_eflip(edgeflip);
		table[TWIST_R][edgeflip] = twist_r_on_eflip(edgeflip);
		table[TWIST_U][edgeflip] = twist_u_on_eflip(edgeflip);
		table[TWIST_B][edgeflip] = twist_b_on_eflip(edgeflip);
		table[TWIST_L][edgeflip] = twist_l_on_eflip(edgeflip);
		table[TWIST_D][edgeflip] = twist_d_on_eflip(edgeflip);
	}

	perm_n_compose(N_EFLIP, table[TWIST_F], table[TWIST_F], table[TWIST_F2]);
	perm_n_compose(N_EFLIP, table[TWIST_R], table[TWIST_R], table[TWIST_R2]);
	perm_n_compose(N_EFLIP, table[TWIST_U], table[TWIST_U], table[TWIST_U2]);
	perm_n_compose(N_EFLIP, table[TWIST_B], table[TWIST_B], table[TWIST_B2]);
	perm_n_compose(N_EFLIP, table[TWIST_L], table[TWIST_L], table[TWIST_L2]);
	perm_n_compose(N_EFLIP, table[TWIST_D], table[TWIST_D], table[TWIST_D2]);

	perm_n_compose(N_EFLIP, table[TWIST_F2], table[TWIST_F], table[TWIST_F3]);
	perm_n_compose(N_EFLIP, table[TWIST_R2], table[TWIST_R], table[TWIST_R3]);
	perm_n_compose(N_EFLIP, table[TWIST_U2], table[TWIST_U], table[TWIST_U3]);
	perm_n_compose(N_EFLIP, table[TWIST_B2], table[TWIST_B], table[TWIST_B3]);
	perm_n_compose(N_EFLIP, table[TWIST_L2], table[TWIST_L], table[TWIST_L3]);
	perm_n_compose(N_EFLIP, table[TWIST_D2], table[TWIST_D], table[TWIST_D3]);

	return;
}


/* ========================================================================= */
int  sym_cu2_on_eflip(int  eflip)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eflip_unpack(eflip, temp_arr);
	two_cycle(temp_arr, EDGE_UF, EDGE_UB);
	two_cycle(temp_arr, EDGE_UR, EDGE_UL);
	two_cycle(temp_arr, EDGE_DF, EDGE_DB);
	two_cycle(temp_arr, EDGE_DR, EDGE_DL);
	two_cycle(temp_arr, EDGE_FR, EDGE_BL);
	two_cycle(temp_arr, EDGE_FL, EDGE_BR);

	return eflip_pack(temp_arr);
}


/* ========================================================================= */
int  sym_cf2_on_eflip(int  eflip)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eflip_unpack(eflip, temp_arr);
	two_cycle(temp_arr, EDGE_UF, EDGE_DF);
	two_cycle(temp_arr, EDGE_UR, EDGE_DL);
	two_cycle(temp_arr, EDGE_UB, EDGE_DB);
	two_cycle(temp_arr, EDGE_UL, EDGE_DR);
	two_cycle(temp_arr, EDGE_FR, EDGE_FL);
	two_cycle(temp_arr, EDGE_BR, EDGE_BL);

	return eflip_pack(temp_arr);
}


/* ========================================================================= */
int  sym_rud_on_eflip(int  eflip)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	eflip_unpack(eflip, temp_arr);
	two_cycle(temp_arr, EDGE_UF, EDGE_DF);
	two_cycle(temp_arr, EDGE_UR, EDGE_DR);
	two_cycle(temp_arr, EDGE_UB, EDGE_DB);
	two_cycle(temp_arr, EDGE_UL, EDGE_DL);

	return eflip_pack(temp_arr);
}


/* ========================================================================= */
void  calc_sym_on_eflip(int  table[N_SYM / 2][N_EFLIP])
/* ------------------------------------------------------------------------- */

{
	int                     edgeflip, sym;


	perm_n_init(N_EFLIP, table[0]);

	for (edgeflip = 0; edgeflip < N_EFLIP; edgeflip++)
		table[1][edgeflip] = sym_cu2_on_eflip(edgeflip);

	for (edgeflip = 0; edgeflip < N_EFLIP; edgeflip++)
		table[2][edgeflip] = sym_cf2_on_eflip(edgeflip);

	perm_n_compose(N_EFLIP, table[2], table[1], table[3]);

	for (edgeflip = 0; edgeflip < N_EFLIP; edgeflip++)
		table[4][edgeflip] = sym_rud_on_eflip(edgeflip);

	for (sym = 5; sym < 8; sym++)
		perm_n_compose(N_EFLIP, table[4], table[sym - 4], table[sym]);

	return;
}


/* ========================================================================= */
int  sym_cu_on_fulledge(int  fulledge)
/* ------------------------------------------------------------------------- */

{
	int                     edgeloc_arr[12], edgeflip_arr[12], temp_arr[12], ii;


	eloc_unpack(fulledge / N_EFLIP, edgeloc_arr);
	eflip_unpack(fulledge % N_EFLIP, edgeflip_arr);

	for (ii = 0; ii < 12; ii++)
		temp_arr[ii] = (edgeloc_arr[ii] != edgeflip_arr[ii]);

	four_cycle(temp_arr, EDGE_UR, EDGE_UB, EDGE_UL, EDGE_UF);
	four_cycle(temp_arr, EDGE_DR, EDGE_DB, EDGE_DL, EDGE_DF);
	four_cycle(temp_arr, EDGE_BR, EDGE_BL, EDGE_FL, EDGE_FR);
	eflip_adjust(temp_arr, EDGE_FR, EDGE_FL, EDGE_BR, EDGE_BL);

	return sym_cu_on_eloc(fulledge / N_EFLIP) * N_EFLIP + eflip_pack(temp_arr);
}


/* ========================================================================= */
void  init_fulledge_to_edgequot(int  sym_on_eloc[N_SYM][N_ELOC],
										  int  sym_on_eflip[N_SYM / 2][N_EFLIP],
										  int  edge_to_fulledge[N_EDGEQUOT],
										  int  stabilizers[N_EDGEQUOT],
										  int  multiplicities[N_EDGEQUOT])
										  /* ------------------------------------------------------------------------- */

{
	int                     count, sym_count, fulledge, cu_fulledge, sym, min_sym;
	int                     min_full, new_full, stab;


	fulledge_to_edge =
		(unsigned short *)malloc(sizeof(unsigned short [N_FULLEDGE]));
	fulledge_to_sym = (unsigned char *)malloc(sizeof(unsigned char [N_FULLEDGE]));

	if ((fulledge_to_edge == NULL) || (fulledge_to_sym == NULL))
		exit_w_error_message("init_fulledge_to_edgequot : couldn't get memory");

	count = 0;

	for (fulledge = 0; fulledge < N_FULLEDGE; fulledge++)
	{
		min_full = fulledge;
		min_sym = 0;
		stab = 0;
		sym_count = 0;
		cu_fulledge = sym_cu_on_fulledge(fulledge);

		for (sym = 0; sym < (N_SYM / 2); sym++)
		{
			new_full = sym_on_eloc[2 * sym][fulledge / N_EFLIP] * N_EFLIP +
				sym_on_eflip[sym][fulledge % N_EFLIP];
			if (min_full > new_full)
			{
				min_full = new_full;
				min_sym = 2 * sym;
			}
			else if (min_full == new_full)
			{
				stab |= (1 << (2 * sym));
				sym_count++;
			}

			new_full = sym_on_eloc[2 * sym][cu_fulledge / N_EFLIP] * N_EFLIP +
				sym_on_eflip[sym][cu_fulledge % N_EFLIP];
			if (min_full > new_full)
			{
				min_full = new_full;
				min_sym = 2 * sym + 1;
			}
			else if (min_full == new_full)
			{
				stab |= (1 << (2 * sym + 1));
				sym_count++;
			}
		}

		if (min_sym == 0)
		{
			if (count >= N_EDGEQUOT)
				exit_w_error_message(
				"init_fulledge_to_edgequot : too many  edgequot's");
			edge_to_fulledge[count] = fulledge;
			stabilizers[count] = stab;
			multiplicities[count] = N_SYM / sym_count;
			fulledge_to_edge[fulledge] = (unsigned short)count;
			fulledge_to_sym[fulledge] = sym_x_invsym_to_sym[0][0];
			count++;
		}
		else
		{
			fulledge_to_edge[fulledge] = fulledge_to_edge[min_full];
			fulledge_to_sym[fulledge] = sym_x_invsym_to_sym[0][min_sym];
		}
	}

	if (count != N_EDGEQUOT)
		exit_w_error_message(
		"init_fulledge_to_edgequot : wrong number of  edgequot's");

	return;
}


/* ========================================================================= */
void  init_twist_on_edge(int  twist_on_eloc[N_TWIST][N_ELOC],
								 int  twist_on_eflip[N_TWIST][N_EFLIP],
								 int  edge_to_fulledge[N_EDGEQUOT])
								 /* ------------------------------------------------------------------------- */

{
	int                     twist, edge, fulledge, new_edge;


	/*  allocate and initialize global arrays         */
	/*  twist_on_edge   and    twist_x_edge_to_sym    */

	twist_on_edge[0] =
		(unsigned short *)malloc(sizeof(unsigned short [N_TWIST][N_EDGEQUOT]));
	twist_x_edge_to_sym[0] =
		(unsigned char *)malloc(sizeof(unsigned char [N_TWIST][N_EDGEQUOT]));

	if ((twist_on_edge[0] == NULL) || (twist_x_edge_to_sym[0] == NULL))
		exit_w_error_message("init_twist_on_edge : couldn't get memory");

	for (twist = 1; twist < N_TWIST; twist++)
	{
		twist_on_edge[twist] = &twist_on_edge[0][twist * N_EDGEQUOT];
		twist_x_edge_to_sym[twist] = &twist_x_edge_to_sym[0][twist * N_EDGEQUOT];
	}

	for (twist = 0; twist < N_TWIST; twist++)
		for (edge = 0; edge < N_EDGEQUOT; edge++)
		{
			fulledge = edge_to_fulledge[edge];
			new_edge = twist_on_eloc[twist][fulledge / N_EFLIP] * N_EFLIP +
				twist_on_eflip[twist][fulledge % N_EFLIP];
			twist_on_edge[twist][edge] = fulledge_to_edge[new_edge];
			twist_x_edge_to_sym[twist][edge] =
				sym_x_invsym_to_sym[0][(int)fulledge_to_sym[new_edge]];
		}

		return;
}


/* ========================================================================= */
void  init_edge_quotient(int  stabilizers[N_EDGEQUOT],
								 int  multiplicities[N_EDGEQUOT])
								 /* ------------------------------------------------------------------------- */

{
	int                     twist_on_eloc[N_TWIST][N_ELOC];
	int                     twist_on_eflip[N_TWIST][N_EFLIP];
	int                     sym_on_eloc[N_SYM][N_ELOC];
	int                     sym_on_eflip[N_SYM / 2][N_EFLIP];
	int                     edge_to_fulledge[N_EDGEQUOT];


	calc_twist_on_eloc(twist_on_eloc);
	calc_sym_on_eloc(sym_on_eloc);
	calc_twist_on_eflip(twist_on_eflip);
	calc_sym_on_eflip(sym_on_eflip);
	init_fulledge_to_edgequot(sym_on_eloc, sym_on_eflip, edge_to_fulledge,
		stabilizers, multiplicities);
	init_twist_on_edge(twist_on_eloc, twist_on_eflip, edge_to_fulledge);

	return;
}


/* ========================================================================= */
void  cornerperm_unpack(int  cperm, int  array_out[8])
/* ------------------------------------------------------------------------- */

{
	perm_n_unpack(8, cperm, array_out);

	return;
}


/* ========================================================================= */
int  cornerperm_pack(int  array_in[8])
/* ------------------------------------------------------------------------- */

{
	return perm_n_pack(8, array_in);
}


/* ========================================================================= */
int  twist_f_on_cperm(int  cperm)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	cornerperm_unpack(cperm, temp_arr);
	four_cycle(temp_arr, CORNER_DFL, CORNER_DRF, CORNER_UFR, CORNER_ULF);

	return cornerperm_pack(temp_arr);
}


/* ========================================================================= */
int  twist_r_on_cperm(int  cperm)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	cornerperm_unpack(cperm, temp_arr);
	four_cycle(temp_arr, CORNER_DRF, CORNER_DBR, CORNER_URB, CORNER_UFR);

	return cornerperm_pack(temp_arr);
}


/* ========================================================================= */
int  twist_u_on_cperm(int  cperm)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	cornerperm_unpack(cperm, temp_arr);
	four_cycle(temp_arr, CORNER_URB, CORNER_UBL, CORNER_ULF, CORNER_UFR);

	return cornerperm_pack(temp_arr);
}


/* ========================================================================= */
int  twist_b_on_cperm(int  cperm)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	cornerperm_unpack(cperm, temp_arr);
	four_cycle(temp_arr, CORNER_DBR, CORNER_DLB, CORNER_UBL, CORNER_URB);

	return cornerperm_pack(temp_arr);
}


/* ========================================================================= */
int  twist_l_on_cperm(int  cperm)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	cornerperm_unpack(cperm, temp_arr);
	four_cycle(temp_arr, CORNER_DLB, CORNER_DFL, CORNER_ULF, CORNER_UBL);

	return cornerperm_pack(temp_arr);
}


/* ========================================================================= */
int  twist_d_on_cperm(int  cperm)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[8];


	cornerperm_unpack(cperm, temp_arr);
	four_cycle(temp_arr, CORNER_DFL, CORNER_DLB, CORNER_DBR, CORNER_DRF);

	return cornerperm_pack(temp_arr);
}


/* ========================================================================= */
void  init_twist_on_cornerperm(void)
/* ------------------------------------------------------------------------- */

{
	int                     cp, twist;


	/*  allocate and initialize global array    twist_on_cornerperm    */

	twist_on_cornerperm[0] =
		(unsigned short *)malloc(sizeof(unsigned short [N_TWIST][N_CORNERPERM]));
	if (twist_on_cornerperm[0] == NULL)
		exit_w_error_message("init_twist_on_cornerperm : couldn't get memory");

	for (twist = 1; twist < N_TWIST; twist++)
		twist_on_cornerperm[twist] = &twist_on_cornerperm[0][twist * N_CORNERPERM];

	for (cp = 0; cp < N_CORNERPERM; cp++)
	{
		twist_on_cornerperm[TWIST_F][cp] = (unsigned short)twist_f_on_cperm(cp);
		twist_on_cornerperm[TWIST_R][cp] = (unsigned short)twist_r_on_cperm(cp);
		twist_on_cornerperm[TWIST_U][cp] = (unsigned short)twist_u_on_cperm(cp);
		twist_on_cornerperm[TWIST_B][cp] = (unsigned short)twist_b_on_cperm(cp);
		twist_on_cornerperm[TWIST_L][cp] = (unsigned short)twist_l_on_cperm(cp);
		twist_on_cornerperm[TWIST_D][cp] = (unsigned short)twist_d_on_cperm(cp);
	}
	for (cp = 0; cp < N_CORNERPERM; cp++)
	{
		twist_on_cornerperm[TWIST_F2][cp] =
			twist_on_cornerperm[TWIST_F][(int)twist_on_cornerperm[TWIST_F][cp]];
		twist_on_cornerperm[TWIST_R2][cp] =
			twist_on_cornerperm[TWIST_R][(int)twist_on_cornerperm[TWIST_R][cp]];
		twist_on_cornerperm[TWIST_U2][cp] =
			twist_on_cornerperm[TWIST_U][(int)twist_on_cornerperm[TWIST_U][cp]];
		twist_on_cornerperm[TWIST_B2][cp] =
			twist_on_cornerperm[TWIST_B][(int)twist_on_cornerperm[TWIST_B][cp]];
		twist_on_cornerperm[TWIST_L2][cp] =
			twist_on_cornerperm[TWIST_L][(int)twist_on_cornerperm[TWIST_L][cp]];
		twist_on_cornerperm[TWIST_D2][cp] =
			twist_on_cornerperm[TWIST_D][(int)twist_on_cornerperm[TWIST_D][cp]];
	}
	for (cp = 0; cp < N_CORNERPERM; cp++)
	{
		twist_on_cornerperm[TWIST_F3][cp] =
			twist_on_cornerperm[TWIST_F2][(int)twist_on_cornerperm[TWIST_F][cp]];
		twist_on_cornerperm[TWIST_R3][cp] =
			twist_on_cornerperm[TWIST_R2][(int)twist_on_cornerperm[TWIST_R][cp]];
		twist_on_cornerperm[TWIST_U3][cp] =
			twist_on_cornerperm[TWIST_U2][(int)twist_on_cornerperm[TWIST_U][cp]];
		twist_on_cornerperm[TWIST_B3][cp] =
			twist_on_cornerperm[TWIST_B2][(int)twist_on_cornerperm[TWIST_B][cp]];
		twist_on_cornerperm[TWIST_L3][cp] =
			twist_on_cornerperm[TWIST_L2][(int)twist_on_cornerperm[TWIST_L][cp]];
		twist_on_cornerperm[TWIST_D3][cp] =
			twist_on_cornerperm[TWIST_D2][(int)twist_on_cornerperm[TWIST_D][cp]];
	}

	return;
}


/* ========================================================================= */
void  sliceedge_unpack(int  sliceedge, int  array_out[12])
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[4], ii, count;


	eloc_unpack(sliceedge % N_ELOC, array_out);
	perm_n_unpack(4, sliceedge / N_ELOC, temp_arr);

	count = 0;
	for (ii = 0; ii < 12; ii++)
		if (array_out[ii] != 0)
		{
			if (count >= 4)
				exit_w_error_message("sliceedge_unpack : corrupted data");

			array_out[ii] = 1 + temp_arr[count++];
		}

		return;
}


/* ========================================================================= */
int  sliceedge_pack(int  array_in[12])
/* ------------------------------------------------------------------------- */

{
	int                     eloc_arr[12], temp_arr[4], ii, count;


	count = 0;
	for (ii = 0; ii < 12; ii++)
	{
		if (array_in[ii] != 0)
		{
			if (count >= 4)
				exit_w_error_message("sliceedge_pack : invalid input");

			temp_arr[count++] = array_in[ii] - 1;
		}

		eloc_arr[ii] = (array_in[ii] != 0);
	}

	return perm_n_pack(4, temp_arr) * N_ELOC + eloc_pack(eloc_arr);
}


/* ========================================================================= */
int  twist_f_on_sliceedge(int  sliceedge)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	sliceedge_unpack(sliceedge, temp_arr);
	four_cycle(temp_arr, EDGE_FL, EDGE_DF, EDGE_FR, EDGE_UF);

	return sliceedge_pack(temp_arr);
}


/* ========================================================================= */
int  twist_r_on_sliceedge(int  sliceedge)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	sliceedge_unpack(sliceedge, temp_arr);
	four_cycle(temp_arr, EDGE_FR, EDGE_DR, EDGE_BR, EDGE_UR);

	return sliceedge_pack(temp_arr);
}


/* ========================================================================= */
int  twist_u_on_sliceedge(int  sliceedge)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	sliceedge_unpack(sliceedge, temp_arr);
	four_cycle(temp_arr, EDGE_UR, EDGE_UB, EDGE_UL, EDGE_UF);

	return sliceedge_pack(temp_arr);
}


/* ========================================================================= */
int  twist_b_on_sliceedge(int  sliceedge)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	sliceedge_unpack(sliceedge, temp_arr);
	four_cycle(temp_arr, EDGE_BR, EDGE_DB, EDGE_BL, EDGE_UB);

	return sliceedge_pack(temp_arr);
}


/* ========================================================================= */
int  twist_l_on_sliceedge(int  sliceedge)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	sliceedge_unpack(sliceedge, temp_arr);
	four_cycle(temp_arr, EDGE_BL, EDGE_DL, EDGE_FL, EDGE_UL);

	return sliceedge_pack(temp_arr);
}


/* ========================================================================= */
int  twist_d_on_sliceedge(int  sliceedge)
/* ------------------------------------------------------------------------- */

{
	int                     temp_arr[12];


	sliceedge_unpack(sliceedge, temp_arr);
	four_cycle(temp_arr, EDGE_DL, EDGE_DB, EDGE_DR, EDGE_DF);

	return sliceedge_pack(temp_arr);
}


/* ========================================================================= */
void  init_twist_on_sliceedge(void)
/* ------------------------------------------------------------------------- */

{
	int                     twist, sl;


	/*  allocate and initialize global array    twist_on_sliceedge    */

	twist_on_sliceedge[0] =
		(unsigned short *)malloc(sizeof(unsigned short [N_TWIST][N_SLICEEDGE]));
	if (twist_on_sliceedge[0] == NULL)
		exit_w_error_message("init_twist_on_sliceedge : couldn't get memory");

	for (twist = 1; twist < N_TWIST; twist++)
		twist_on_sliceedge[twist] = &twist_on_sliceedge[0][twist * N_SLICEEDGE];

	for (sl = 0; sl < N_SLICEEDGE; sl++)
	{
		twist_on_sliceedge[TWIST_F][sl] = (unsigned short)twist_f_on_sliceedge(sl);
		twist_on_sliceedge[TWIST_R][sl] = (unsigned short)twist_r_on_sliceedge(sl);
		twist_on_sliceedge[TWIST_U][sl] = (unsigned short)twist_u_on_sliceedge(sl);
		twist_on_sliceedge[TWIST_B][sl] = (unsigned short)twist_b_on_sliceedge(sl);
		twist_on_sliceedge[TWIST_L][sl] = (unsigned short)twist_l_on_sliceedge(sl);
		twist_on_sliceedge[TWIST_D][sl] = (unsigned short)twist_d_on_sliceedge(sl);
	}
	for (sl = 0; sl < N_SLICEEDGE; sl++)
	{
		twist_on_sliceedge[TWIST_F2][sl] =
			twist_on_sliceedge[TWIST_F][(int)twist_on_sliceedge[TWIST_F][sl]];
		twist_on_sliceedge[TWIST_R2][sl] =
			twist_on_sliceedge[TWIST_R][(int)twist_on_sliceedge[TWIST_R][sl]];
		twist_on_sliceedge[TWIST_U2][sl] =
			twist_on_sliceedge[TWIST_U][(int)twist_on_sliceedge[TWIST_U][sl]];
		twist_on_sliceedge[TWIST_B2][sl] =
			twist_on_sliceedge[TWIST_B][(int)twist_on_sliceedge[TWIST_B][sl]];
		twist_on_sliceedge[TWIST_L2][sl] =
			twist_on_sliceedge[TWIST_L][(int)twist_on_sliceedge[TWIST_L][sl]];
		twist_on_sliceedge[TWIST_D2][sl] =
			twist_on_sliceedge[TWIST_D][(int)twist_on_sliceedge[TWIST_D][sl]];
	}
	for (sl = 0; sl < N_SLICEEDGE; sl++)
	{
		twist_on_sliceedge[TWIST_F3][sl] =
			twist_on_sliceedge[TWIST_F2][(int)twist_on_sliceedge[TWIST_F][sl]];
		twist_on_sliceedge[TWIST_R3][sl] =
			twist_on_sliceedge[TWIST_R2][(int)twist_on_sliceedge[TWIST_R][sl]];
		twist_on_sliceedge[TWIST_U3][sl] =
			twist_on_sliceedge[TWIST_U2][(int)twist_on_sliceedge[TWIST_U][sl]];
		twist_on_sliceedge[TWIST_B3][sl] =
			twist_on_sliceedge[TWIST_B2][(int)twist_on_sliceedge[TWIST_B][sl]];
		twist_on_sliceedge[TWIST_L3][sl] =
			twist_on_sliceedge[TWIST_L2][(int)twist_on_sliceedge[TWIST_L][sl]];
		twist_on_sliceedge[TWIST_D3][sl] =
			twist_on_sliceedge[TWIST_D2][(int)twist_on_sliceedge[TWIST_D][sl]];
	}

	return;
}


/* ========================================================================= */
int  make_current(int  corner, int  edge, Search_data  *p_data)
/* ------------------------------------------------------------------------- */

{
	if (DIST(corner, edge) > p_data->depth)
	{
		if (edge & 0x1)
		{
			distance[corner][edge >> 1] &= 0x0F;
			distance[corner][edge >> 1] |= (unsigned char)((p_data->depth) << 4);
		}
		else
		{
			distance[corner][edge >> 1] &= 0xF0;
			distance[corner][edge >> 1] |= (unsigned char)(p_data->depth);
		}

		p_data->found_quot++;
		p_data->found += (p_data->multiplicities)[edge];

		return 1;
	}

	return 0;
}


/* ========================================================================= */
void  make_current_all(int  corner, int  edge, Search_data  *p_data)
/* ------------------------------------------------------------------------- */

{
	int                     sym, stab;


	if (make_current(corner, edge, p_data))
	{
		stab = (p_data->stabilizers)[edge];
		for (sym = 1; sym < N_SYM; sym++)
		{
			stab /= 2;

			if (stab % 2)
				make_current((int)sym_on_corner[sym][corner], edge, p_data);
		}
	}

	return;
}


/* ========================================================================= */
void  make_neighbors_current(int  corner, int  edge, Search_data  *p_data )
/* ------------------------------------------------------------------------- */

{
	int                     twist, new_edge, new_corner, sym;


	for (twist = 0; twist < N_TWIST; twist++)
	{
		if (p_current_metric.twist_length[twist] != 1)
			continue;

		new_edge = (int)twist_on_edge[twist][edge];
		sym = (int)twist_x_edge_to_sym[twist][edge];
		new_corner = (int)sym_on_corner[sym][(int)twist_on_corner[twist][corner]];
		make_current_all(new_corner, new_edge, p_data);
	}

	return;
}


/* ========================================================================= */
int  neighbors_are_previous(int  corner, int  edge, Search_data  *p_data)
/* ------------------------------------------------------------------------- */

{
	int                     twist, new_edge, sym, new_corner;


	for (twist = 0; twist < N_TWIST; twist++)
	{
		if (p_current_metric.twist_length[twist] != 1)
			continue;

		new_edge = (int)twist_on_edge[twist][edge];
		sym = (int)twist_x_edge_to_sym[twist][edge];
		new_corner = (int)sym_on_corner[sym][(int)twist_on_corner[twist][corner]];

		if (DIST(new_corner, new_edge) < p_data->depth)
			return 1;
	}

	return 0;
}


/* ========================================================================= */
void  init_distance_table(int  edge_stabilizers[N_EDGEQUOT],
								  int  edge_multiplicities[N_EDGEQUOT])
								  /* ------------------------------------------------------------------------- */

{
	Search_data             sdata_struc;
	int                     total_found_quot, corner, edge, ii, msg_given;


	/*  allocate and initialize global array   distance   */

	distance[0] = (unsigned char *)malloc(sizeof(unsigned char [N_DIST_CHARS]));
	if (distance[0] == NULL)
		exit_w_error_message("init_distance_table : couldn't get memory");

	for (corner = 1; corner < N_CORNER; corner++)
		distance[corner] = &distance[0][corner * (N_EDGEQUOT / 2)];

	for (ii = 0; ii < N_DIST_CHARS; ii++)
		distance[0][ii] = (unsigned char)255;

	msg_given = 0;

	sdata_struc.depth = 0;
	sdata_struc.found_quot = 0;
	sdata_struc.found = 0;
	sdata_struc.stabilizers = edge_stabilizers;
	sdata_struc.multiplicities = edge_multiplicities;
	total_found_quot = 0;

	//printf("distance     positions     (quotient)\n");

	make_current_all(CORNER_START, EDGE_START, &sdata_struc);

	while (sdata_struc.found)
	{
		//printf("%7d%c %13d     (%8d)\n", sdata_struc.depth,
		//p_current_metric.metric_char, sdata_struc.found,
		//                                 sdata_struc.found_quot);
		total_found_quot += sdata_struc.found_quot;
		sdata_struc.found_quot = 0;
		sdata_struc.found = 0;

		if (++(sdata_struc.depth) == 15)
			break;                            /*  shouldn't happen  */

		if (total_found_quot == 2 * N_DIST_CHARS)
			break;

		if (total_found_quot < BACKWARDS_SWITCH_POINT)  /*  search forward  */
		{
			for (corner = 0; corner < N_CORNER; corner++)
				for (edge = 0; edge < N_EDGEQUOT; edge++)
					if (DIST(corner, edge) == sdata_struc.depth - 1)
						make_neighbors_current(corner, edge, &sdata_struc);
		}
		else  /*  search backward  */
		{
			if (msg_given == 0)
			{
				//printf("     switching to backwards searching\n");
				msg_given = 1;
			}

			for (corner = 0; corner < N_CORNER; corner++)
				for (edge = 0; edge < N_EDGEQUOT; edge++)
					if ((DIST(corner, edge) == 15) &&
						neighbors_are_previous(corner, edge, &sdata_struc))
						make_current_all(corner, edge, &sdata_struc);
		}
	}

	return;
}

/* ========================================================================= */
void  __declspec(dllexport) init_globals(void)
/* ------------------------------------------------------------------------- */

{

	int                    *edge_stabilizers;
	int                    *edge_multiplicities;


	init_sym_x_invsym_to_sym();
	init_invsym_on_twist();
	init_twist_on_follow();
	init_twist_on_corner();
	init_sym_on_corner();

	edge_stabilizers = (int *)malloc(sizeof(int [2 * N_EDGEQUOT]));
	if (edge_stabilizers == NULL)
		exit_w_error_message("init_globals : couldn't get memory");

	edge_multiplicities = &edge_stabilizers[N_EDGEQUOT];

	init_edge_quotient(edge_stabilizers, edge_multiplicities);
	init_twist_on_cornerperm();
	init_twist_on_sliceedge();
	//printf("initializing distance table ... this will take several minutes\n");
	init_distance_table(edge_stabilizers, edge_multiplicities);

	free((void *)edge_stabilizers);

	return;
}

/* ========================================================================= */
int  __declspec(dllexport) save_globals(char * FileName)
/* ------------------------------------------------------------------------- */
{
	FILE * pFile;
	char buffer[BUFSIZ];

	pFile = fopen (FileName,"wb");
	if (pFile==NULL)
		return -1;

	setbuf ( pFile , buffer );
	/*  initialize global array   sym_x_invsym_to_sym   */
	fwrite(sym_x_invsym_to_sym[0],sizeof(unsigned char [N_SYM][N_SYM]),1,pFile);

	/*   invsym_on_twist_ud ,  invsym_on_twist_fb   and   invsym_on_twist_rl   */
	fwrite(invsym_on_twist_ud[0],sizeof(unsigned char [3][N_SYM][N_TWIST]),1,pFile);

	/*  allocate and initialize global array   twist_on_corner    */
	fwrite(twist_on_corner[0],sizeof(unsigned short [N_TWIST][N_CORNER]),1,pFile);

	/*  allocate and initialize global array   sym_on_corner    */
	fwrite(sym_on_corner[0],sizeof(unsigned short [N_SYM][N_CORNER]),1,pFile);

	/* allocate and init fulledge_to_edge/fulledge_to_sym */
	fwrite(fulledge_to_edge,sizeof(unsigned short [N_FULLEDGE]),1,pFile);
	fwrite(fulledge_to_sym ,sizeof(unsigned char [N_FULLEDGE]),1,pFile);

	/*  twist_on_edge   and    twist_x_edge_to_sym    */
	fwrite(twist_on_edge[0],sizeof(unsigned short [N_TWIST][N_EDGEQUOT]),1,pFile);

	fwrite(twist_x_edge_to_sym[0],sizeof(unsigned char [N_TWIST][N_EDGEQUOT]),1,pFile);

	/*  allocate and initialize global array    twist_on_cornerperm    */
	fwrite(twist_on_cornerperm[0],sizeof(unsigned short [N_TWIST][N_CORNERPERM]),1,pFile);

	/*  allocate and initialize global array    twist_on_sliceedge    */
	fwrite(twist_on_sliceedge[0],sizeof(unsigned short [N_TWIST][N_SLICEEDGE]),1,pFile);
	fwrite(twist_on_follow[0], sizeof(unsigned short [N_TWIST][N_FOLLOW]),1,pFile);
	fwrite(distance[0],sizeof(unsigned char [N_DIST_CHARS]),1,pFile);

	fclose (pFile);
	return 0;

}



/* ========================================================================= */
int  __declspec(dllexport) restore_globals(char * FileName)
/* ------------------------------------------------------------------------- */
{
	FILE * pFile;
	unsigned char         (*mem_ptr_uc2)[N_SYM][N_TWIST];
	int ii,sym,twist,corner;
	unsigned short        (*mem_ptr_us1)[N_FOLLOW];
	unsigned char         (*mem_ptr_uc)[N_SYM];
	long lSize;
	char buffer[BUFSIZ];

	pFile = fopen (FileName,"rb");

	if (pFile==NULL)
		return -1;
	setbuf ( pFile , buffer );


	// obtain file size:
	fseek (pFile , 0 , SEEK_END);
	lSize = ftell (pFile);
	rewind(pFile);

	if(lSize != 79035169)
		return -3;

	/*  initialize global array   sym_x_invsym_to_sym   */

	mem_ptr_uc = (unsigned char (*)[N_SYM])malloc(sizeof(unsigned char [N_SYM][N_SYM]));
	if (mem_ptr_uc == NULL)
		return -2;
	for (ii = 0; ii < N_SYM; ii++)
		sym_x_invsym_to_sym[ii] = mem_ptr_uc[ii];




	/*   allocate and initialize global arrays                                 */
	/*   invsym_on_twist_ud ,  invsym_on_twist_fb   and   invsym_on_twist_rl   */

	mem_ptr_uc2 = (unsigned char (*)[N_SYM][N_TWIST])
		malloc(sizeof(unsigned char [3][N_SYM][N_TWIST]));
	if (mem_ptr_uc2 == NULL)
		return -2;
	for (sym = 0; sym < N_SYM; sym++)
	{
		invsym_on_twist_ud[sym] = mem_ptr_uc2[0][sym];
		invsym_on_twist_rl[sym] = mem_ptr_uc2[1][sym];
		invsym_on_twist_fb[sym] = mem_ptr_uc2[2][sym];
	}



	/*  allocate and initialize global array   twist_on_corner    */

	twist_on_corner[0] =
		(unsigned short *)malloc(sizeof(unsigned short [N_TWIST][N_CORNER]));
	if (twist_on_corner[0] == NULL)
		return -2;
	for (twist = 1; twist < N_TWIST; twist++)
		twist_on_corner[twist] = &twist_on_corner[0][twist * N_CORNER];



	/*  allocate and initialize global array   sym_on_corner    */

	sym_on_corner[0] = (unsigned short *)malloc(sizeof(unsigned short [N_SYM][N_CORNER]));
	if (sym_on_corner[0] == NULL)	
		return -2;
	for (sym = 1; sym < N_SYM; sym++)
		sym_on_corner[sym] = &sym_on_corner[0][sym * N_CORNER];




	fulledge_to_edge = (unsigned short *)malloc(sizeof(unsigned short [N_FULLEDGE]));
	fulledge_to_sym = (unsigned char *)malloc(sizeof(unsigned char [N_FULLEDGE]));

	if ((fulledge_to_edge == NULL) || (fulledge_to_sym == NULL))
		return -2;





	/*  allocate and initialize global arrays         */
	/*  twist_on_edge   and    twist_x_edge_to_sym    */

	twist_on_edge[0] =
		(unsigned short *)malloc(sizeof(unsigned short [N_TWIST][N_EDGEQUOT]));
	twist_x_edge_to_sym[0] =
		(unsigned char *)malloc(sizeof(unsigned char [N_TWIST][N_EDGEQUOT]));

	if ((twist_on_edge[0] == NULL) || (twist_x_edge_to_sym[0] == NULL))
		return -2;
	for (twist = 1; twist < N_TWIST; twist++)
	{
		twist_on_edge[twist] = &twist_on_edge[0][twist * N_EDGEQUOT];
		twist_x_edge_to_sym[twist] = &twist_x_edge_to_sym[0][twist * N_EDGEQUOT];
	}











	/*  allocate and initialize global array    twist_on_cornerperm    */

	twist_on_cornerperm[0] =
		(unsigned short *)malloc(sizeof(unsigned short [N_TWIST][N_CORNERPERM]));
	if (twist_on_cornerperm[0] == NULL)
		return -2;
	for (twist = 1; twist < N_TWIST; twist++)
		twist_on_cornerperm[twist] = &twist_on_cornerperm[0][twist * N_CORNERPERM];










	/*  allocate and initialize global array    twist_on_sliceedge    */
	twist_on_sliceedge[0] =
		(unsigned short *)malloc(sizeof(unsigned short [N_TWIST][N_SLICEEDGE]));
	if (twist_on_sliceedge[0] == NULL)
		return -2;

	for (twist = 1; twist < N_TWIST; twist++)
		twist_on_sliceedge[twist] = &twist_on_sliceedge[0][twist * N_SLICEEDGE];










	mem_ptr_us1 = (unsigned short (*)[N_FOLLOW])
		malloc(sizeof(unsigned short [N_TWIST][N_FOLLOW]));
	if (mem_ptr_us1 == NULL)
		return -2;
	for (twist = 0; twist < N_TWIST; twist++)
		twist_on_follow[twist] = mem_ptr_us1[twist];




	distance[0] = (unsigned char *)malloc(sizeof(unsigned char [N_DIST_CHARS]));
	if (distance[0] == NULL)
		return -2;
	for (corner = 1; corner < N_CORNER; corner++)
		distance[corner] = &distance[0][corner * (N_EDGEQUOT / 2)];






	/*  initialize global array   sym_x_invsym_to_sym   */
	fread(sym_x_invsym_to_sym[0],sizeof(unsigned char [N_SYM][N_SYM]),1,pFile);

	/*   invsym_on_twist_ud ,  invsym_on_twist_fb   and   invsym_on_twist_rl   */
	fread(invsym_on_twist_ud[0],sizeof(unsigned char [3][N_SYM][N_TWIST]),1,pFile);

	/*  allocate and initialize global array   twist_on_corner    */
	fread(twist_on_corner[0],sizeof(unsigned short [N_TWIST][N_CORNER]),1,pFile);

	/*  allocate and initialize global array   sym_on_corner    */
	fread(sym_on_corner[0],sizeof(unsigned short [N_SYM][N_CORNER]),1,pFile);

	/* allocate and init fulledge_to_edge/fulledge_to_sym */
	fread(fulledge_to_edge,sizeof(unsigned short [N_FULLEDGE]),1,pFile);
	fread(fulledge_to_sym ,sizeof(unsigned char [N_FULLEDGE]),1,pFile);

	/*  twist_on_edge   and    twist_x_edge_to_sym    */
	fread(twist_on_edge[0],sizeof(unsigned short [N_TWIST][N_EDGEQUOT]),1,pFile);
	fread(twist_x_edge_to_sym[0],sizeof(unsigned char [N_TWIST][N_EDGEQUOT]),1,pFile);

	/*  allocate and initialize global array    twist_on_cornerperm    */
	fread(twist_on_cornerperm[0],sizeof(unsigned short [N_TWIST][N_CORNERPERM]),1,pFile);

	/*  allocate and initialize global array    twist_on_sliceedge    */
	fread(twist_on_sliceedge[0],sizeof(unsigned short [N_TWIST][N_SLICEEDGE]),1,pFile);
	fread(twist_on_follow[0], sizeof(unsigned short [N_TWIST][N_FOLLOW]),1,pFile);
	fread(distance[0],sizeof(unsigned char [N_DIST_CHARS]),1,pFile);

	fclose (pFile);
	return 0;

}


/* ========================================================================= */
int __declspec(dllexport) string_to_cube(char  string[], Cube  *p_cube, int  give_err_msg)
/* ------------------------------------------------------------------------- */

/*  input:  string[]  */

{
	char                    edge_str[12][3], corner_str[8][4];
	int                     edge_arr[12], corner_arr[8];
	int                     ii, jj, twist, flip, edge_par, corner_par, stat;


	stat = 0;

	if (sscanf(string, "%2s %2s %2s %2s %2s %2s %2s %2s %2s %2s %2s %2s %3s %3s %3s %3s %3s %3s %3s %3s",
		edge_str[0], edge_str[1], edge_str[2], edge_str[3],
		edge_str[4], edge_str[5], edge_str[6], edge_str[7],
		edge_str[8], edge_str[9], edge_str[10], edge_str[11],
		corner_str[0], corner_str[1], corner_str[2], corner_str[3],
		corner_str[4], corner_str[5], corner_str[6], corner_str[7]) < 20)
	{
		//if (give_err_msg)
		//printf("invalid input!\n");
		return 1;
	}

	for (ii = 0; ii < 12; ii++)
	{
		for (jj = 0; jj < 24; jj++)
			if (strcmp(edge_str[ii], edge_cubie_str[jj]) == 0)
			{
				p_cube->edges[ii] = jj;
				break;
			}
			if (jj == 24)
			{
				//if (give_err_msg)
				//printf("invalid edge cubie: %s\n", edge_str[ii]);
				stat = 1;
			}
	}

	for (ii = 0; ii < 8; ii++)
	{
		for (jj = 0; jj < 24; jj++)
			if (strcmp(corner_str[ii], corner_cubie_str[jj]) == 0)
			{
				p_cube->corners[ii] = jj;
				break;
			}
			if (jj == 24)
			{
				//if (give_err_msg)
				//printf("invalid corner cubie: %s\n", corner_str[ii]);
				stat = 1;
			}
	}

	if (stat)
		return stat;

	/*  fill out the remaining oriented edges and corners  */

	for (ii = 12; ii < 24; ii++)
		p_cube->edges[ii] = (12 + p_cube->edges[ii - 12]) % 24;

	for (ii = 8; ii < 24; ii++)
		p_cube->corners[ii] = (8 + p_cube->corners[ii - 8]) % 24;

	/*  now check to see that it's a legal cube  */

	if (perm_n_check(24, p_cube->edges))
	{
		//if (give_err_msg)
		//printf("bad edge cubies\n");
		stat = 1;
	}

	if (perm_n_check(24, p_cube->corners))
	{
		//if (give_err_msg)
		//printf("bad corner cubies\n");
		stat = 1;
	}

	if (stat)
		return stat;

	flip = 0;
	for (ii = 0; ii < 12; ii++)
		flip += (p_cube->edges[ii] / 12);

	if ((flip % 2) != 0)
	{
		//if (give_err_msg)
		//printf("flip any edge cubie!\n");
		stat = 1;
	}

	twist = 0;
	for (ii = 0; ii < 8; ii++)
		twist += (p_cube->corners[ii] / 8);

	twist %= 3;

	if (twist != 0)
	{
		//if (give_err_msg)
		//   printf("twist any corner cubie %sclockwise!\n",
		//           (twist == 1) ? "counter" : "");
		stat = 1;
	}

	for (ii = 0; ii < 12; ii++)
		edge_arr[ii] = p_cube->edges[ii] % 12;

	edge_par = perm_n_parity(12, edge_arr);

	for (ii = 0; ii < 8; ii++)
		corner_arr[ii] = p_cube->corners[ii] % 8;

	corner_par = perm_n_parity(8, corner_arr);

	if (edge_par != corner_par)
	{
		//if (give_err_msg)
		//printf("swap any two edge cubies or any two corner cubies!\n");
		stat = 1;
	}

	return stat;
}


/* ========================================================================= */
int  user_enters_cube(Cube  *p_cube)
/* ------------------------------------------------------------------------- */

{
	char                     line_str[256];


	//printf("\nenter cube (Ctrl-D to exit):\n");

	line_str[0] = '\n';

	while (line_str[0] == '\n')           /*  ignore blank lines  */
	{
		if (fgets(line_str, 256, stdin) == NULL)
			return -1;

		if (line_str[0] == '%')         /*  echo comments  */
		{
			//printf("%s", line_str);
			line_str[0] = '\n';
		}
	}

	return string_to_cube(line_str, p_cube, 1);
}


/* ========================================================================= */
void  calc_cube_urf(Cube  *p_cube)
/* ------------------------------------------------------------------------- */

{
	cube_init(p_cube);
	three_cycle(p_cube->edges, EDGE_UF, EDGE_FR, EDGE_RU);
	three_cycle(p_cube->edges, EDGE_UB, EDGE_FL, EDGE_RD);
	three_cycle(p_cube->edges, EDGE_DB, EDGE_BL, EDGE_LD);
	three_cycle(p_cube->edges, EDGE_DF, EDGE_BR, EDGE_LU);
	three_cycle(p_cube->edges, EDGE_FU, EDGE_RF, EDGE_UR);
	three_cycle(p_cube->edges, EDGE_BU, EDGE_LF, EDGE_DR);
	three_cycle(p_cube->edges, EDGE_BD, EDGE_LB, EDGE_DL);
	three_cycle(p_cube->edges, EDGE_FD, EDGE_RB, EDGE_UL);
	three_cycle(p_cube->corners, CORNER_UFR, CORNER_FRU, CORNER_RUF);
	three_cycle(p_cube->corners, CORNER_DLB, CORNER_BDL, CORNER_LBD);
	three_cycle(p_cube->corners, CORNER_URB, CORNER_FUL, CORNER_RFD);
	three_cycle(p_cube->corners, CORNER_UBL, CORNER_FLD, CORNER_RDB);
	three_cycle(p_cube->corners, CORNER_RBU, CORNER_ULF, CORNER_FDR);
	three_cycle(p_cube->corners, CORNER_BLU, CORNER_LDF, CORNER_DBR);
	three_cycle(p_cube->corners, CORNER_BUR, CORNER_LFU, CORNER_DRF);
	three_cycle(p_cube->corners, CORNER_LUB, CORNER_DFL, CORNER_BRD);

	return;
}


/* ========================================================================= */
void  cube_to_coset_coord(Cube  *p_cube, Coset_coord  *p_coset_coord)
/* ------------------------------------------------------------------------- */

/*  output:  *p_coset_coord  */

{
	int                     corner_arr[8], edge_arr[12];
	int                     ii, eflip, eloc, sym, corner;


	for (ii = 0; ii < 12; ii++)
		edge_arr[ii] = p_cube->edges[ii] / 12;
	eflip = eflip_pack(edge_arr);

	for (ii = 0; ii < 12; ii++)
		edge_arr[ii] = (p_cube->edges[ii] % 12) / 8;
	eloc = eloc_pack(edge_arr);

	p_coset_coord->edge_state = (int)fulledge_to_edge[eloc * N_EFLIP + eflip];
	p_coset_coord->sym_state = sym = (int)fulledge_to_sym[eloc * N_EFLIP + eflip];

	for (ii = 0; ii < 8; ii++)
		corner_arr[ii] = p_cube->corners[ii] / 8;

	corner = corner_pack(corner_arr);
	p_coset_coord->corner_state =
		(int)sym_on_corner[(int)sym_x_invsym_to_sym[0][sym]][corner];

	return;
}


/* ========================================================================= */
void  process_full_cube(Full_cube  *p_cube)
/* ------------------------------------------------------------------------- */

{
	Cube                    cube1, cube2, cube_urf;
	int                     edges_to_ud[12], edges_to_rl[12], edges_to_fb[12];
	int                     cornerperm_arr[8], sliceedge_arr[12], ii;


	/*  p_cube->cubies  already filled in  */
	/*  fill in other fields of  p_cube    */

	for (ii = 0; ii < 8; ii++)
		cornerperm_arr[ii] = p_cube->cubies.corners[ii] % 8;

	p_cube->cornerperm = cornerperm_pack(cornerperm_arr);
	p_cube->parity = perm_n_parity(8, cornerperm_arr);


	for (ii = 0; ii < 12; ii++)
		edges_to_ud[ii] = edges_to_fb[ii] = edges_to_rl[ii] = 0;

	edges_to_ud[EDGE_FR] = 1;
	edges_to_ud[EDGE_FL] = 2;
	edges_to_ud[EDGE_BR] = 3;
	edges_to_ud[EDGE_BL] = 4;

	edges_to_rl[EDGE_UF] = 1;
	edges_to_rl[EDGE_UB] = 2;
	edges_to_rl[EDGE_DF] = 3;
	edges_to_rl[EDGE_DB] = 4;

	edges_to_fb[EDGE_UR] = 1;
	edges_to_fb[EDGE_UL] = 2;
	edges_to_fb[EDGE_DR] = 3;
	edges_to_fb[EDGE_DL] = 4;


	for (ii = 0; ii < 12; ii++)
		sliceedge_arr[ii] = edges_to_ud[(p_cube->cubies.edges[ii]) % 12];
	p_cube->ud_sliceedge = sliceedge_pack(sliceedge_arr);

	for (ii = 0; ii < 12; ii++)
		sliceedge_arr[ii] = edges_to_fb[(p_cube->cubies.edges[ii]) % 12];
	p_cube->fb_sliceedge = sliceedge_pack(sliceedge_arr);

	for (ii = 0; ii < 12; ii++)
		sliceedge_arr[ii] = edges_to_rl[(p_cube->cubies.edges[ii]) % 12];
	p_cube->rl_sliceedge = sliceedge_pack(sliceedge_arr);


	cube_to_coset_coord(&p_cube->cubies, &p_cube->ud);

	calc_cube_urf(&cube_urf);

	cube_conjugate(&p_cube->cubies, &cube_urf, &cube1);
	cube_to_coset_coord(&cube1, &p_cube->rl);

	cube_conjugate(&cube1, &cube_urf, &cube2);
	cube_to_coset_coord(&cube2, &p_cube->fb);

	return;
}


/* ========================================================================= */
void  set_cube_symmetry(Full_cube  *p_cube, Cube  sym_cubes[N_CUBESYM],
								int  subgroup_list[N_SYMSUBGRP][N_CUBESYM], Options cOptions)
								/* ------------------------------------------------------------------------- */

{
	Cube                    temp_cube;
	int                     subgroup[N_CUBESYM], sym, count;


	if (cOptions.use_symmetry)
	{
		count = 0;

		for (sym = 0; sym < N_CUBESYM; sym++)
		{
			cube_conjugate(&p_cube->cubies, &sym_cubes[sym], &temp_cube);
			subgroup[sym] = (cube_compare(&p_cube->cubies, &temp_cube) == 0);
			if (subgroup[sym])
				count++;
		}

		p_cube->sym_subgrp = which_subgroup(subgroup, subgroup_list);

		if (p_cube->sym_subgrp < 0)
			exit_w_error_message("set_cube_symmetry : unknown symmetry group");

		//if (count > 1)
		//   printf("position has %d-fold symmetry (subgroup #%d)\n", count,
		//                                                        p_cube->sym_subgrp);
		//else
		//   printf("asymmetric position\n");
	}
	else
		p_cube->sym_subgrp = SUBGRP_TRIVIAL;

	return;
}

/* ========================================================================= */
void  output_solution(Search_node  *node_arr)
/* ------------------------------------------------------------------------- */

{
	static char            *twist_string[] = {"F ", "F2", "F'", "R ", "R2", "R'",
		"U ", "U2", "U'", "B ", "B2", "B'",
		"L ", "L2", "L'", "D ", "D2", "D'"};
	Search_node            *p_node;
	int                     turn_list[MAX_TWISTS];
	int                     ii, count, q_length, f_length;


	count = 0;

	for (p_node = node_arr; p_node->remain_depth > 0; p_node++)
		turn_list[count++] = p_node[1].twist;

	q_length = f_length = 0;

	for (ii = 0; ii < count; ii++)
	{
		q_length += metric_q_length(turn_list[ii]);
		f_length += metric_f_length(turn_list[ii]);
	}
	//
	//if(SolutionCallback != NULL)
	//	SolutionCallback(turn_list,count);
	//
	//for (ii = 0; ii < count; ii++)
	//    printf(" %s", twist_string[turn_list[ii]]);
	//
	//if (p_current_metric.metric == QUARTER_TURN_METRIC)
	//   printf("  (%dq*, %df)\n", q_length, f_length);
	//else
	//   printf("  (%df*, %dq)\n", f_length, q_length);
	fflush(stdout);

	sol_found = 1;

	return;
}


/* ========================================================================= */
int  test_for_solution(Full_cube  *p_cube, Search_node  *node_arr)
/* ------------------------------------------------------------------------- */

{
	register Search_node   *p_node;
	register int            cornerperm, sliceedge;


	n_tests++;

	cornerperm = p_cube->cornerperm;
	for (p_node = node_arr; p_node->remain_depth > 0; p_node++)
		cornerperm = (int)twist_on_cornerperm[p_node[1].twist][cornerperm];

	if (cornerperm != CORNERPERM_START)
		return 0;                                  /*  not a solution  */

	sliceedge = p_cube->ud_sliceedge;
	for (p_node = node_arr; p_node->remain_depth > 0; p_node++)
		sliceedge = (int)twist_on_sliceedge[p_node[1].twist][sliceedge];

	if (sliceedge != UD_SLICEEDGE_START)
		return 0;                                  /*  not a solution  */

	sliceedge = p_cube->rl_sliceedge;
	for (p_node = node_arr; p_node->remain_depth > 0; p_node++)
		sliceedge = (int)twist_on_sliceedge[p_node[1].twist][sliceedge];

	if (sliceedge != RL_SLICEEDGE_START)
		return 0;                                  /*  not a solution  */

	sliceedge = p_cube->fb_sliceedge;
	for (p_node = node_arr; p_node->remain_depth > 0; p_node++)
		sliceedge = (int)twist_on_sliceedge[p_node[1].twist][sliceedge];

	if (sliceedge != FB_SLICEEDGE_START)
		return 0;                                  /*  not a solution  */

	output_solution(node_arr);                    /*  solution !  */

	return 1;
}


/* ========================================================================= */
Search_node * search_tree(Full_cube  *p_cube, Search_node  *node_arr, Options cOptions)
/* ------------------------------------------------------------------------- */

{
	register Search_node   *p_node;
	register int            twist, virtual_twist, new_sym_factor;


	p_node = node_arr;

	while (p_node >= node_arr)
	{
		if (p_node->remain_depth == 0)
		{
			if (test_for_solution(p_cube, node_arr) &&
				cOptions.one_solution_only)
				return p_node;
			p_node--;
		}
		else
		{
			for (twist = p_node[1].twist + 1; twist < N_TWIST; twist++)
			{
				p_node[1].follow_type =
					(int)twist_on_follow[twist][p_node->follow_type];

				if (p_node[1].follow_type == FOLLOW_INVALID)
					continue;

				p_node[1].remain_depth = p_node->remain_depth -
					p_current_metric.twist_length[twist];
				if (p_node[1].remain_depth < 0)
					continue;

				n_nodes++;

				virtual_twist =
					(int)invsym_on_twist_ud[p_node->ud.sym_state][twist];
				new_sym_factor =
					(int)twist_x_edge_to_sym[virtual_twist][p_node->ud.edge_state];
				p_node[1].ud.edge_state =
					(int)twist_on_edge[virtual_twist][p_node->ud.edge_state];
				p_node[1].ud.sym_state =
					(int)sym_x_invsym_to_sym[p_node->ud.sym_state][new_sym_factor];
				p_node[1].ud.corner_state = (int)sym_on_corner[new_sym_factor]
				[(int)twist_on_corner[virtual_twist][p_node->ud.corner_state]];

				if (p_node[1].remain_depth <
					DIST(p_node[1].ud.corner_state, p_node[1].ud.edge_state))
					continue;


				virtual_twist =
					(int)invsym_on_twist_rl[p_node->rl.sym_state][twist];
				new_sym_factor =
					(int)twist_x_edge_to_sym[virtual_twist][p_node->rl.edge_state];
				p_node[1].rl.edge_state =
					(int)twist_on_edge[virtual_twist][p_node->rl.edge_state];
				p_node[1].rl.sym_state =
					(int)sym_x_invsym_to_sym[p_node->rl.sym_state][new_sym_factor];
				p_node[1].rl.corner_state = (int)sym_on_corner[new_sym_factor]
				[(int)twist_on_corner[virtual_twist][p_node->rl.corner_state]];

				if (p_node[1].remain_depth <
					DIST(p_node[1].rl.corner_state, p_node[1].rl.edge_state))
					continue;


				virtual_twist =
					(int)invsym_on_twist_fb[p_node->fb.sym_state][twist];
				new_sym_factor =
					(int)twist_x_edge_to_sym[virtual_twist][p_node->fb.edge_state];
				p_node[1].fb.edge_state =
					(int)twist_on_edge[virtual_twist][p_node->fb.edge_state];
				p_node[1].fb.sym_state =
					(int)sym_x_invsym_to_sym[p_node->fb.sym_state][new_sym_factor];
				p_node[1].fb.corner_state = (int)sym_on_corner[new_sym_factor]
				[(int)twist_on_corner[virtual_twist][p_node->fb.corner_state]];

				if (p_node[1].remain_depth <
					DIST(p_node[1].fb.corner_state, p_node[1].fb.edge_state))
					continue;


				p_node[1].twist = twist;
				break;
			}

			if (twist == N_TWIST)
				p_node--;
			else
			{
				p_node++;
				p_node[1].twist = -1;
			}
		}
	}

	return 0;
}


/* ========================================================================= */
void  calc_sym_cubes(Cube  sym_conj[N_CUBESYM])
/* ------------------------------------------------------------------------- */

{
	int                     ii;


	cube_init(&sym_conj[0]);
	cube_init(&sym_conj[1]);

	four_cycle(sym_conj[1].edges, EDGE_UF, EDGE_UL, EDGE_UB, EDGE_UR);
	four_cycle(sym_conj[1].edges, EDGE_DF, EDGE_DL, EDGE_DB, EDGE_DR);
	four_cycle(sym_conj[1].edges, EDGE_FR, EDGE_LF, EDGE_BL, EDGE_RB);
	four_cycle(sym_conj[1].edges, EDGE_FU, EDGE_LU, EDGE_BU, EDGE_RU);
	four_cycle(sym_conj[1].edges, EDGE_FD, EDGE_LD, EDGE_BD, EDGE_RD);
	four_cycle(sym_conj[1].edges, EDGE_RF, EDGE_FL, EDGE_LB, EDGE_BR);
	four_cycle(sym_conj[1].corners, CORNER_UFR, CORNER_ULF, CORNER_UBL, CORNER_URB);
	four_cycle(sym_conj[1].corners, CORNER_DRF, CORNER_DFL, CORNER_DLB, CORNER_DBR);
	four_cycle(sym_conj[1].corners, CORNER_FRU, CORNER_LFU, CORNER_BLU, CORNER_RBU);
	four_cycle(sym_conj[1].corners, CORNER_RFD, CORNER_FLD, CORNER_LBD, CORNER_BRD);
	four_cycle(sym_conj[1].corners, CORNER_RUF, CORNER_FUL, CORNER_LUB, CORNER_BUR);
	four_cycle(sym_conj[1].corners, CORNER_FDR, CORNER_LDF, CORNER_BDL, CORNER_RDB);

	for (ii = 2; ii < 4; ii++)
		cube_compose(&sym_conj[1], &sym_conj[ii - 1], &sym_conj[ii]);

	cube_init(&sym_conj[4]);

	two_cycle(sym_conj[4].edges, EDGE_UF, EDGE_DF);
	two_cycle(sym_conj[4].edges, EDGE_UR, EDGE_DL);
	two_cycle(sym_conj[4].edges, EDGE_UB, EDGE_DB);
	two_cycle(sym_conj[4].edges, EDGE_UL, EDGE_DR);
	two_cycle(sym_conj[4].edges, EDGE_FR, EDGE_FL);
	two_cycle(sym_conj[4].edges, EDGE_BR, EDGE_BL);
	two_cycle(sym_conj[4].edges, EDGE_FU, EDGE_FD);
	two_cycle(sym_conj[4].edges, EDGE_RU, EDGE_LD);
	two_cycle(sym_conj[4].edges, EDGE_BU, EDGE_BD);
	two_cycle(sym_conj[4].edges, EDGE_LU, EDGE_RD);
	two_cycle(sym_conj[4].edges, EDGE_RF, EDGE_LF);
	two_cycle(sym_conj[4].edges, EDGE_RB, EDGE_LB);
	two_cycle(sym_conj[4].corners, CORNER_UFR, CORNER_DFL);
	two_cycle(sym_conj[4].corners, CORNER_ULF, CORNER_DRF);
	two_cycle(sym_conj[4].corners, CORNER_UBL, CORNER_DBR);
	two_cycle(sym_conj[4].corners, CORNER_URB, CORNER_DLB);
	two_cycle(sym_conj[4].corners, CORNER_FRU, CORNER_FLD);
	two_cycle(sym_conj[4].corners, CORNER_LFU, CORNER_RFD);
	two_cycle(sym_conj[4].corners, CORNER_BLU, CORNER_BRD);
	two_cycle(sym_conj[4].corners, CORNER_RBU, CORNER_LBD);
	two_cycle(sym_conj[4].corners, CORNER_RUF, CORNER_LDF);
	two_cycle(sym_conj[4].corners, CORNER_FUL, CORNER_FDR);
	two_cycle(sym_conj[4].corners, CORNER_LUB, CORNER_RDB);
	two_cycle(sym_conj[4].corners, CORNER_BUR, CORNER_BDL);

	for (ii = 5; ii < 8; ii++)
		cube_compose(&sym_conj[4], &sym_conj[ii - 4], &sym_conj[ii]);

	cube_init(&sym_conj[8]);

	two_cycle(sym_conj[8].edges, EDGE_UF, EDGE_DF);
	two_cycle(sym_conj[8].edges, EDGE_UR, EDGE_DR);
	two_cycle(sym_conj[8].edges, EDGE_UB, EDGE_DB);
	two_cycle(sym_conj[8].edges, EDGE_UL, EDGE_DL);
	two_cycle(sym_conj[8].edges, EDGE_FU, EDGE_FD);
	two_cycle(sym_conj[8].edges, EDGE_RU, EDGE_RD);
	two_cycle(sym_conj[8].edges, EDGE_BU, EDGE_BD);
	two_cycle(sym_conj[8].edges, EDGE_LU, EDGE_LD);
	two_cycle(sym_conj[8].corners, CORNER_UFR, CORNER_DRF);
	two_cycle(sym_conj[8].corners, CORNER_ULF, CORNER_DFL);
	two_cycle(sym_conj[8].corners, CORNER_UBL, CORNER_DLB);
	two_cycle(sym_conj[8].corners, CORNER_URB, CORNER_DBR);
	two_cycle(sym_conj[8].corners, CORNER_FRU, CORNER_FDR);
	two_cycle(sym_conj[8].corners, CORNER_LFU, CORNER_LDF);
	two_cycle(sym_conj[8].corners, CORNER_BLU, CORNER_BDL);
	two_cycle(sym_conj[8].corners, CORNER_RBU, CORNER_RDB);
	two_cycle(sym_conj[8].corners, CORNER_RUF, CORNER_RFD);
	two_cycle(sym_conj[8].corners, CORNER_FUL, CORNER_FLD);
	two_cycle(sym_conj[8].corners, CORNER_LUB, CORNER_LBD);
	two_cycle(sym_conj[8].corners, CORNER_BUR, CORNER_BRD);

	for (ii = 9; ii < 16; ii++)
		cube_compose(&sym_conj[8], &sym_conj[ii - 8], &sym_conj[ii]);

	calc_cube_urf(&sym_conj[16]);

	for (ii = 17; ii < 48; ii++)
		cube_compose(&sym_conj[16], &sym_conj[ii - 16], &sym_conj[ii]);

	return;
}


/* ========================================================================= */
void  pretty_print_unsigned_int(unsigned int  nn)
/* ------------------------------------------------------------------------- */

{
	int                     digits[4], ii, started;


	for (ii = 0; ii < 4; ii++)
	{
		digits[ii] = nn % 1000;
		nn /= 1000;
	}

	started = 0;

	for (ii = 3; ii >= 0; ii--)
	{
		if (started)
		{
			//if (digits[ii] >= 100)
			//   printf("%3d", digits[ii]);
			//else if (digits[ii] >= 10)
			//        printf("0%2d", digits[ii]);
			//else
			//   printf("00%1d", digits[ii]);
		}
		else
		{
			if (digits[ii] >= 100)
			{
				//printf("%3d", digits[ii]);
				started = 1;
			}
			else if (digits[ii] >= 10)
			{
				//printf(" %2d", digits[ii]);
				started = 1;
			}
			else if ((digits[ii] >= 1) || (ii == 0))
			{
				//printf("  %1d", digits[ii]);
				started = 1;
			}
			//else
			//printf("   ");
		}

		//if (ii > 0)
		//   printf("%c", started ? ',' : ' ');
	}

	return;
}


/* ========================================================================= */
int __declspec(dllexport) solve_cube(Cube  *p_cube, Options cOptions, int * SolutionLength, int  Solution[])
/* ------------------------------------------------------------------------- */

{
	static Cube             sym_cubes[N_CUBESYM];
	static int              subgroup_list[N_SYMSUBGRP][N_CUBESYM];
	static int              initialized = 0;
	Full_cube               full_cube_struct;
	Search_node             node_arr[MAX_TWISTS];
	int                     ii, start_depth, search_limit;
	//int turn_list[MAX_TWISTS];
	/*	int                     count;*/
	Search_node            *p_node;





	if (initialized == 0)
	{
		calc_sym_cubes(sym_cubes);
		calc_subgroup_list(subgroup_list);
		initialized = 1;
	}


	print_cube(p_cube);
	if (cube_is_solved(p_cube))
	{
		//printf("cube is already solved!\n");

		//if(SolutionCallback != NULL)
		//	SolutionCallback(turn_list,0);
		*SolutionLength = 0;
		return 1;
	}

	cube_conjugate(p_cube, &sym_cubes[0], &full_cube_struct.cubies);

	process_full_cube(&full_cube_struct);

	set_cube_symmetry(&full_cube_struct, sym_cubes, subgroup_list, cOptions);

	node_arr[0].follow_type = 1 + full_cube_struct.sym_subgrp;
	node_arr[0].ud.corner_state = full_cube_struct.ud.corner_state;
	node_arr[0].ud.edge_state = full_cube_struct.ud.edge_state;
	node_arr[0].ud.sym_state = full_cube_struct.ud.sym_state;

	node_arr[0].rl.corner_state = full_cube_struct.rl.corner_state;
	node_arr[0].rl.edge_state = full_cube_struct.rl.edge_state;
	node_arr[0].rl.sym_state = full_cube_struct.rl.sym_state;

	node_arr[0].fb.corner_state = full_cube_struct.fb.corner_state;
	node_arr[0].fb.edge_state = full_cube_struct.fb.edge_state;
	node_arr[0].fb.sym_state = full_cube_struct.fb.sym_state;

	sol_found = 0;
	if ((p_current_metric.metric == QUARTER_TURN_METRIC) &&
		(full_cube_struct.parity == 0))
		start_depth = 2;
	else
		start_depth = 1;

	search_limit = cOptions.search_limit;
	if ((search_limit <= 0) || (search_limit >= MAX_TWISTS))
		search_limit = MAX_TWISTS - 1;

	for (ii = start_depth; ii <= search_limit; ii += p_current_metric.increment)
	{
		n_nodes = (long long int)0;
		n_tests = (unsigned int)0;
		node_arr[0].remain_depth = ii;
		node_arr[1].twist = -1;
		if(search_tree(&full_cube_struct, node_arr, cOptions))
		{		 
			*SolutionLength = 0;

			for (p_node = node_arr; p_node->remain_depth > 0; p_node++)
				Solution[(*SolutionLength)++] = p_node[1].twist;

			return 1;
		}


		if ((cOptions.one_solution_only == 0) || (sol_found == 0))
		{
			//printf("depth %2d%c completed  (", ii, p_current_metric.metric_char);
			pretty_print_unsigned_int(n_nodes);
			//printf(" nodes, ");
			pretty_print_unsigned_int(n_tests);
			//printf(" tests)\n");
			fflush(stdout);
		}

		if (sol_found)
			break;
	}
	return 0;
}



/* ========================================================================= */
int __declspec(dllexport) main(void)
/* ------------------------------------------------------------------------- */

{
	Metric_data             metric_data;
	Options                 user_options;
	Cube                    cube_struct;
	int                     stat;
	int							SolutionLength;
	int							Solution[MAX_TWISTS];

	init_options(&metric_data, &user_options);
	init_globals();

	signal(SIGINT, SIG_IGN);

	while (1)
	{
		stat = user_enters_cube(&cube_struct);
		if (stat < 0)
			break;

		if (stat == 0)
		{
			//if (sigsetjmp(jump_env, 1) == 0)
			{
				signal(SIGINT, user_interrupt);
				solve_cube(&cube_struct, user_options,&SolutionLength,Solution);
			}

			signal(SIGINT, SIG_IGN);
		}
	}

	exit(EXIT_SUCCESS);

	return 0;  /*  haha  */
}
